diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index 5d606a2a663..b89ca5eb953 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -29,7 +29,7 @@ jobs: with: go-version: ${{ matrix.goversion }} - run: go install -v ./gnovm/cmd/gno - - run: go run ./gnovm/cmd/gno transpile -v --gobuild ./examples + - run: go run ./gnovm/cmd/gno transpile -v --gobuild ./examples/... test: strategy: fail-fast: false diff --git a/contribs/gnodev/pkg/dev/node.go b/contribs/gnodev/pkg/dev/node.go index 12a88490515..3ebcd657b6d 100644 --- a/contribs/gnodev/pkg/dev/node.go +++ b/contribs/gnodev/pkg/dev/node.go @@ -16,7 +16,7 @@ import ( "github.com/gnolang/gno/gno.land/pkg/gnoland" "github.com/gnolang/gno/gno.land/pkg/gnoland/ugnot" "github.com/gnolang/gno/gno.land/pkg/integration" - "github.com/gnolang/gno/gnovm/pkg/gnomod" + "github.com/gnolang/gno/gnovm/pkg/packages" "github.com/gnolang/gno/tm2/pkg/amino" tmcfg "github.com/gnolang/gno/tm2/pkg/bft/config" "github.com/gnolang/gno/tm2/pkg/bft/node" @@ -44,6 +44,7 @@ type NodeConfig struct { MaxGasPerBlock int64 ChainID string ChainDomain string + LoadConfig *packages.LoadConfig } func DefaultNodeConfig(rootdir, domain string) *NodeConfig { @@ -70,6 +71,7 @@ func DefaultNodeConfig(rootdir, domain string) *NodeConfig { TMConfig: tmc, SkipFailingGenesisTxs: true, MaxGasPerBlock: 10_000_000_000, + LoadConfig: &packages.LoadConfig{Deps: true}, } } @@ -98,7 +100,7 @@ type Node struct { var DefaultFee = std.NewFee(50000, std.MustParseCoin(ugnot.ValueString(1000000))) func NewDevNode(ctx context.Context, cfg *NodeConfig) (*Node, error) { - mpkgs, err := NewPackagesMap(cfg.PackagesPathList) + mpkgs, err := NewPackagesMap(cfg.LoadConfig, cfg.PackagesPathList) if err != nil { return nil, fmt.Errorf("unable map pkgs list: %w", err) } @@ -140,7 +142,7 @@ func (n *Node) Close() error { return n.Node.Stop() } -func (n *Node) ListPkgs() []gnomod.Pkg { +func (n *Node) ListPkgs() []*packages.Package { n.muNode.RLock() defer n.muNode.RUnlock() @@ -240,7 +242,7 @@ func (n *Node) updatePackages(paths ...string) error { } // List all packages from target path - pkgslist, err := gnomod.ListPkgs(abspath) + pkgslist, err := packages.Load(n.config.LoadConfig, filepath.Join(abspath, "...")) if err != nil { return fmt.Errorf("failed to list gno packages for %q: %w", path, err) } @@ -248,12 +250,12 @@ func (n *Node) updatePackages(paths ...string) error { // Update or add package in the current known list. for _, pkg := range pkgslist { n.pkgs[pkg.Dir] = Package{ - Pkg: pkg, + Package: pkg, Creator: deployer, Deposit: deposit, } - n.logger.Debug("pkgs update", "name", pkg.Name, "path", pkg.Dir) + n.logger.Debug("pkgs update", "name", pkg.ImportPath, "path", pkg.Dir) } pkgsUpdated += len(pkgslist) diff --git a/contribs/gnodev/pkg/dev/node_state_test.go b/contribs/gnodev/pkg/dev/node_state_test.go index efaeb979693..c5993bfb390 100644 --- a/contribs/gnodev/pkg/dev/node_state_test.go +++ b/contribs/gnodev/pkg/dev/node_state_test.go @@ -158,7 +158,7 @@ func Render(_ string) string { return strconv.Itoa(value) } // Call NewDevNode with no package should work node, emitter := newTestingDevNode(t, counterPkg) - assert.Len(t, node.ListPkgs(), 1) + assert.Len(t, node.ListPkgs(), 23) // Test rendering render, err := testingRenderRealm(t, node, testCounterRealm) diff --git a/contribs/gnodev/pkg/dev/node_test.go b/contribs/gnodev/pkg/dev/node_test.go index 4a4acc232b9..e4183231408 100644 --- a/contribs/gnodev/pkg/dev/node_test.go +++ b/contribs/gnodev/pkg/dev/node_test.go @@ -499,7 +499,6 @@ func newTestingDevNodeWithConfig(t *testing.T, cfg *NodeConfig) (*Node, *mock.Se node, err := NewDevNode(ctx, cfg) require.NoError(t, err) - assert.Len(t, node.ListPkgs(), len(cfg.PackagesPathList)) t.Cleanup(func() { node.Close() diff --git a/contribs/gnodev/pkg/dev/packages.go b/contribs/gnodev/pkg/dev/packages.go index 62c1907b8c9..41ba499458d 100644 --- a/contribs/gnodev/pkg/dev/packages.go +++ b/contribs/gnodev/pkg/dev/packages.go @@ -10,8 +10,8 @@ import ( "github.com/gnolang/gno/contribs/gnodev/pkg/address" "github.com/gnolang/gno/gno.land/pkg/gnoland" vmm "github.com/gnolang/gno/gno.land/pkg/sdk/vm" - gno "github.com/gnolang/gno/gnovm/pkg/gnolang" - "github.com/gnolang/gno/gnovm/pkg/gnomod" + "github.com/gnolang/gno/gnovm/pkg/gnolang" + "github.com/gnolang/gno/gnovm/pkg/packages" "github.com/gnolang/gno/tm2/pkg/crypto" "github.com/gnolang/gno/tm2/pkg/std" ) @@ -63,7 +63,7 @@ func ResolvePackagePathQuery(bk *address.Book, path string) (PackagePath, error) } type Package struct { - gnomod.Pkg + *packages.Package Creator crypto.Address Deposit std.Coins } @@ -75,7 +75,7 @@ var ( ErrEmptyDepositPackage = errors.New("no deposit specified for package") ) -func NewPackagesMap(ppaths []PackagePath) (PackagesMap, error) { +func NewPackagesMap(cfg *packages.LoadConfig, ppaths []PackagePath) (PackagesMap, error) { pkgs := make(map[string]Package) for _, ppath := range ppaths { if ppath.Creator.IsZero() { @@ -88,7 +88,7 @@ func NewPackagesMap(ppaths []PackagePath) (PackagesMap, error) { } // list all packages from target path - pkgslist, err := gnomod.ListPkgs(abspath) + pkgslist, err := packages.Load(cfg, filepath.Join(abspath, "...")) if err != nil { return nil, fmt.Errorf("listing gno packages: %w", err) } @@ -102,7 +102,7 @@ func NewPackagesMap(ppaths []PackagePath) (PackagesMap, error) { continue // skip } pkgs[pkg.Dir] = Package{ - Pkg: pkg, + Package: pkg, Creator: ppath.Creator, Deposit: ppath.Deposit, } @@ -112,10 +112,10 @@ func NewPackagesMap(ppaths []PackagePath) (PackagesMap, error) { return pkgs, nil } -func (pm PackagesMap) toList() gnomod.PkgList { - list := make([]gnomod.Pkg, 0, len(pm)) +func (pm PackagesMap) toList() packages.PkgList { + list := make([]*packages.Package, 0, len(pm)) for _, pkg := range pm { - list = append(list, pkg.Pkg) + list = append(list, pkg.Package) } return list } @@ -132,13 +132,17 @@ func (pm PackagesMap) Load(fee std.Fee, start time.Time) ([]gnoland.TxWithMetada metatxs := make([]gnoland.TxWithMetadata, 0, len(nonDraft)) for _, modPkg := range nonDraft { + if modPkg.ImportPath == "" || gnolang.IsStdlib(modPkg.ImportPath) { + continue + } + pkg := pm[modPkg.Dir] if pkg.Creator.IsZero() { return nil, fmt.Errorf("no creator set for %q", pkg.Dir) } // Open files in directory as MemPackage. - memPkg := gno.MustReadMemPackage(modPkg.Dir, modPkg.Name) + memPkg := gnolang.MustReadMemPackage(modPkg.Dir, modPkg.ImportPath) if err := memPkg.Validate(); err != nil { return nil, fmt.Errorf("invalid package: %w", err) } diff --git a/contribs/gnodev/pkg/watcher/watch.go b/contribs/gnodev/pkg/watcher/watch.go index 63158a06c4b..a518271f358 100644 --- a/contribs/gnodev/pkg/watcher/watch.go +++ b/contribs/gnodev/pkg/watcher/watch.go @@ -11,9 +11,9 @@ import ( emitter "github.com/gnolang/gno/contribs/gnodev/pkg/emitter" events "github.com/gnolang/gno/contribs/gnodev/pkg/events" + "github.com/gnolang/gno/gnovm/pkg/packages" "github.com/fsnotify/fsnotify" - "github.com/gnolang/gno/gnovm/pkg/gnomod" ) type PackageWatcher struct { @@ -118,7 +118,7 @@ func (p *PackageWatcher) Stop() { // Packages are sorted by their length in descending order to facilitate easier // and more efficient matching with corresponding paths. The longest paths are // compared first. -func (p *PackageWatcher) AddPackages(pkgs ...gnomod.Pkg) error { +func (p *PackageWatcher) AddPackages(pkgs ...*packages.Package) error { for _, pkg := range pkgs { dir := pkg.Dir diff --git a/contribs/gnogenesis/internal/txs/txs_add_packages.go b/contribs/gnogenesis/internal/txs/txs_add_packages.go index 53c0bb4b686..daabc726bf0 100644 --- a/contribs/gnogenesis/internal/txs/txs_add_packages.go +++ b/contribs/gnogenesis/internal/txs/txs_add_packages.go @@ -7,12 +7,12 @@ import ( "fmt" "os" - "github.com/gnolang/gno/tm2/pkg/crypto/keys" - "github.com/gnolang/gno/gno.land/pkg/gnoland" "github.com/gnolang/gno/gno.land/pkg/gnoland/ugnot" + "github.com/gnolang/gno/gnovm/pkg/packages" "github.com/gnolang/gno/tm2/pkg/bft/types" "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/gnolang/gno/tm2/pkg/crypto/keys" "github.com/gnolang/gno/tm2/pkg/std" ) @@ -125,7 +125,8 @@ func execTxsAddPackages( parsedTxs := make([]gnoland.TxWithMetadata, 0) for _, path := range args { // Generate transactions from the packages (recursively) - txs, err := gnoland.LoadPackagesFromDir(path, creator, genesisDeployFee) + loadCfg := &packages.LoadConfig{IO: io, Deps: true} + txs, err := gnoland.LoadPackagesFromDir(loadCfg, path, creator, genesisDeployFee) if err != nil { return fmt.Errorf("unable to load txs from directory, %w", err) } diff --git a/examples/Makefile b/examples/Makefile index cdc73ee6b3a..3f351b8052e 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -23,21 +23,21 @@ GOIMPORTS_FLAGS ?= $(GOFMT_FLAGS) GOTEST_FLAGS ?= -v -p 1 -timeout=30m # Official packages (non-overridable): more reliable and tested modules, distinct from the experimentation area. -OFFICIAL_PACKAGES = ./gno.land/p -OFFICIAL_PACKAGES += ./gno.land/r/demo -OFFICIAL_PACKAGES += ./gno.land/r/gnoland -OFFICIAL_PACKAGES += ./gno.land/r/sys -OFFICIAL_PACKAGES += ./gno.land/r/gov +OFFICIAL_PACKAGES = ./gno.land/p/... +OFFICIAL_PACKAGES += ./gno.land/r/demo/... +OFFICIAL_PACKAGES += ./gno.land/r/gnoland/... +OFFICIAL_PACKAGES += ./gno.land/r/sys/... +OFFICIAL_PACKAGES += ./gno.land/r/gov/... ######################################## # Dev tools .PHONY: transpile transpile: - go run ../gnovm/cmd/gno transpile -v . + go run ../gnovm/cmd/gno transpile -v ./... .PHONY: build build: - go run ../gnovm/cmd/gno transpile -v --gobuild . + go run ../gnovm/cmd/gno transpile -v --gobuild ./... .PHONY: test test: @@ -45,7 +45,7 @@ test: .PHONY: lint lint: - go run ../gnovm/cmd/gno lint -v $(OFFICIAL_PACKAGES) + go run ../gnovm/cmd/gno lint --root-examples -v $(OFFICIAL_PACKAGES) .PHONY: test.sync test.sync: diff --git a/examples/gno.land/p/moul/md/md_test.gno b/examples/gno.land/p/moul/md/md_test.gno index 144ae58d918..3e8e70efaf6 100644 --- a/examples/gno.land/p/moul/md/md_test.gno +++ b/examples/gno.land/p/moul/md/md_test.gno @@ -1,4 +1,4 @@ -package md +package md_test import ( "testing" diff --git a/examples/gno.land/r/gnoland/faucet/faucet_test.gno b/examples/gno.land/r/gnoland/faucet/faucet_test.gno index cecbb2ebcd6..5672f317469 100644 --- a/examples/gno.land/r/gnoland/faucet/faucet_test.gno +++ b/examples/gno.land/r/gnoland/faucet/faucet_test.gno @@ -1,4 +1,4 @@ -package faucet +package faucet_test import ( "std" diff --git a/examples/no_cycles_test.go b/examples/no_cycles_test.go new file mode 100644 index 00000000000..04069194304 --- /dev/null +++ b/examples/no_cycles_test.go @@ -0,0 +1,117 @@ +package examples_test + +import ( + "fmt" + "path/filepath" + "slices" + "strings" + "testing" + + "github.com/gnolang/gno/gnovm/pkg/gnoenv" + "github.com/gnolang/gno/gnovm/pkg/packages" + "github.com/stretchr/testify/require" +) + +var injectedTestingLibs = []string{"encoding/json", "fmt", "os", "internal/os_test"} + +// TestNoCycles checks that there is no import cycles in stdlibs and non-draft examples +func TestNoCycles(t *testing.T) { + // find examples and stdlibs + cfg := &packages.LoadConfig{SelfContained: true, Deps: true} + pkgs, err := packages.Load(cfg, filepath.Join(gnoenv.RootDir(), "examples", "...")) + require.NoError(t, err) + + // detect cycles + visited := make(map[string]bool) + for _, p := range pkgs { + if p.Draft { + continue + } + require.NoError(t, detectCycles(p, pkgs, visited)) + } +} + +// detectCycles detects import cycles +// +// We need to check +// 3 kinds of nodes +// +// - normal pkg: compiled source +// +// - xtest pkg: external test source (include xtests and filetests), can be treated as their own package +// +// - test pkg: embedded test sources, +// these should not have their corresponding normal package in their dependencies tree +// +// The tricky thing is that we need to split test sources and normal source +// while not considering them as distincitive packages. +// Otherwise we will have false positive for example if we have these edges: +// +// - foo_pkg/foo_test.go imports bar_pkg +// +// - bar_pkg/bar_test.go import foo_pkg +// +// In go, the above example is allowed +// but the following is not +// +// - foo_pkg/foo.go imports bar_pkg +// +// - bar_pkg/bar_test.go imports foo_pkg +func detectCycles(root *packages.Package, pkgs []*packages.Package, visited map[string]bool) error { + // check cycles in package's sources + stack := []string{} + if err := visitPackage(root, pkgs, visited, stack); err != nil { + return fmt.Errorf("pkgsrc import: %w", err) + } + // check cycles in external tests' dependencies we might have missed + if err := visitImports([]packages.FileKind{packages.FileKindXTest, packages.FileKindFiletest}, root, pkgs, visited, stack); err != nil { + return fmt.Errorf("xtest import: %w", err) + } + + // check cycles in tests' imports by marking the current package as visited while visiting the tests' imports + // we also consider PackageSource imports here because tests can call package code + visited = map[string]bool{root.ImportPath: true} + stack = []string{root.ImportPath} + if err := visitImports([]packages.FileKind{packages.FileKindPackageSource, packages.FileKindTest}, root, pkgs, visited, stack); err != nil { + return fmt.Errorf("test import: %w", err) + } + + return nil +} + +// visitImports resolves and visits imports by kinds +func visitImports(kinds []packages.FileKind, root *packages.Package, pkgs []*packages.Package, visited map[string]bool, stack []string) error { + for _, imp := range root.Imports.Merge(kinds...) { + if slices.Contains(injectedTestingLibs, imp) { + continue + } + idx := slices.IndexFunc(pkgs, func(p *packages.Package) bool { return p.ImportPath == imp }) + if idx == -1 { + return fmt.Errorf("import %q not found for %q tests", imp, root.ImportPath) + } + if err := visitPackage(pkgs[idx], pkgs, visited, stack); err != nil { + return fmt.Errorf("test import error: %w", err) + } + } + + return nil +} + +// visitNode visits a package and its imports recursively. It only considers imports in PackageSource +func visitPackage(pkg *packages.Package, pkgs []*packages.Package, visited map[string]bool, stack []string) error { + if slices.Contains(stack, pkg.ImportPath) { + return fmt.Errorf("cycle detected: %s -> %s", strings.Join(stack, " -> "), pkg.ImportPath) + } + if visited[pkg.ImportPath] { + return nil + } + + visited[pkg.ImportPath] = true + stack = append(stack, pkg.ImportPath) + + if err := visitImports([]packages.FileKind{packages.FileKindPackageSource}, pkg, pkgs, visited, stack); err != nil { + return err + } + + return nil +} diff --git a/gno.land/cmd/gnoland/start.go b/gno.land/cmd/gnoland/start.go index 4f380031be4..87fc4f7b79a 100644 --- a/gno.land/cmd/gnoland/start.go +++ b/gno.land/cmd/gnoland/start.go @@ -17,6 +17,7 @@ import ( "github.com/gnolang/gno/gno.land/pkg/gnoland/ugnot" "github.com/gnolang/gno/gno.land/pkg/log" "github.com/gnolang/gno/gnovm/pkg/gnoenv" + "github.com/gnolang/gno/gnovm/pkg/packages" abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" "github.com/gnolang/gno/tm2/pkg/bft/config" "github.com/gnolang/gno/tm2/pkg/bft/node" @@ -355,7 +356,7 @@ func lazyInitGenesis( } // Generate the new genesis.json file - if err := generateGenesisFile(genesisPath, privateKey, c); err != nil { + if err := generateGenesisFile(io, genesisPath, privateKey, c); err != nil { return fmt.Errorf("unable to generate genesis file, %w", err) } @@ -380,7 +381,7 @@ func initializeLogger(io io.WriteCloser, logLevel, logFormat string) (*zap.Logge return log.GetZapLoggerFn(format)(io, level), nil } -func generateGenesisFile(genesisFile string, privKey crypto.PrivKey, c *startCfg) error { +func generateGenesisFile(io commands.IO, genesisFile string, privKey crypto.PrivKey, c *startCfg) error { var ( pubKey = privKey.PubKey() // There is an active constraint for gno.land transactions: @@ -425,7 +426,8 @@ func generateGenesisFile(genesisFile string, privKey crypto.PrivKey, c *startCfg // Load examples folder examplesDir := filepath.Join(c.gnoRootDir, "examples") - pkgsTxs, err := gnoland.LoadPackagesFromDir(examplesDir, txSender, genesisDeployFee) + loadCfg := &packages.LoadConfig{IO: io, SelfContained: true} + pkgsTxs, err := gnoland.LoadPackagesFromDir(loadCfg, examplesDir, txSender, genesisDeployFee) if err != nil { return fmt.Errorf("unable to load examples folder: %w", err) } diff --git a/gno.land/pkg/gnoclient/integration_test.go b/gno.land/pkg/gnoclient/integration_test.go index bfcaaec999e..70929fefb92 100644 --- a/gno.land/pkg/gnoclient/integration_test.go +++ b/gno.land/pkg/gnoclient/integration_test.go @@ -710,7 +710,7 @@ func loadpkgs(t *testing.T, rootdir string, paths ...string) []gnoland.TxWithMet for _, path := range paths { path = filepath.Clean(path) path = filepath.Join(examplesDir, path) - err := loader.LoadPackage(examplesDir, path, "") + err := loader.LoadPackage(path, "") require.NoErrorf(t, err, "`loadpkg` unable to load package(s) from %q: %s", path, err) } privKey, err := integration.GeneratePrivKeyFromMnemonic(integration.DefaultAccount_Seed, "", 0, 0) diff --git a/gno.land/pkg/gnoland/genesis.go b/gno.land/pkg/gnoland/genesis.go index a754e7a4644..02fea917cee 100644 --- a/gno.land/pkg/gnoland/genesis.go +++ b/gno.land/pkg/gnoland/genesis.go @@ -3,11 +3,12 @@ package gnoland import ( "errors" "fmt" + "path/filepath" "strings" vmm "github.com/gnolang/gno/gno.land/pkg/sdk/vm" - gno "github.com/gnolang/gno/gnovm/pkg/gnolang" - "github.com/gnolang/gno/gnovm/pkg/gnomod" + "github.com/gnolang/gno/gnovm/pkg/gnolang" + "github.com/gnolang/gno/gnovm/pkg/packages" "github.com/gnolang/gno/tm2/pkg/amino" bft "github.com/gnolang/gno/tm2/pkg/bft/types" "github.com/gnolang/gno/tm2/pkg/crypto" @@ -133,9 +134,9 @@ func LoadGenesisTxsFile(path string, chainID string, genesisRemote string) ([]Tx // LoadPackagesFromDir loads gno packages from a directory. // It creates and returns a list of transactions based on these packages. -func LoadPackagesFromDir(dir string, creator bft.Address, fee std.Fee) ([]TxWithMetadata, error) { +func LoadPackagesFromDir(cfg *packages.LoadConfig, dir string, creator bft.Address, fee std.Fee) ([]TxWithMetadata, error) { // list all packages from target path - pkgs, err := gnomod.ListPkgs(dir) + pkgs, err := packages.Load(cfg, filepath.Join(dir, "...")) if err != nil { return nil, fmt.Errorf("listing gno packages: %w", err) } @@ -150,6 +151,10 @@ func LoadPackagesFromDir(dir string, creator bft.Address, fee std.Fee) ([]TxWith nonDraftPkgs := sortedPkgs.GetNonDraftPkgs() txs := make([]TxWithMetadata, 0, len(nonDraftPkgs)) for _, pkg := range nonDraftPkgs { + if pkg.ImportPath == "" || gnolang.IsStdlib(pkg.ImportPath) { + continue + } + tx, err := LoadPackage(pkg, creator, fee, nil) if err != nil { return nil, fmt.Errorf("unable to load package %q: %w", pkg.Dir, err) @@ -164,11 +169,11 @@ func LoadPackagesFromDir(dir string, creator bft.Address, fee std.Fee) ([]TxWith } // LoadPackage loads a single package into a `std.Tx` -func LoadPackage(pkg gnomod.Pkg, creator bft.Address, fee std.Fee, deposit std.Coins) (std.Tx, error) { +func LoadPackage(pkg *packages.Package, creator bft.Address, fee std.Fee, deposit std.Coins) (std.Tx, error) { var tx std.Tx // Open files in directory as MemPackage. - memPkg := gno.MustReadMemPackage(pkg.Dir, pkg.Name) + memPkg := gnolang.MustReadMemPackage(pkg.Dir, pkg.ImportPath) err := memPkg.Validate() if err != nil { return tx, fmt.Errorf("invalid package: %w", err) diff --git a/gno.land/pkg/integration/node_testing.go b/gno.land/pkg/integration/node_testing.go index edcf53de5d3..6f89593bddb 100644 --- a/gno.land/pkg/integration/node_testing.go +++ b/gno.land/pkg/integration/node_testing.go @@ -8,6 +8,7 @@ import ( "github.com/gnolang/gno/gno.land/pkg/gnoland" "github.com/gnolang/gno/gno.land/pkg/gnoland/ugnot" + "github.com/gnolang/gno/gnovm/pkg/packages" abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" tmcfg "github.com/gnolang/gno/tm2/pkg/bft/config" "github.com/gnolang/gno/tm2/pkg/bft/node" @@ -135,7 +136,8 @@ func LoadDefaultPackages(t TestingTS, creator bft.Address, gnoroot string) []gno examplesDir := filepath.Join(gnoroot, "examples") defaultFee := std.NewFee(50000, std.MustParseCoin(ugnot.ValueString(1000000))) - txs, err := gnoland.LoadPackagesFromDir(examplesDir, creator, defaultFee) + cfg := &packages.LoadConfig{SelfContained: true} + txs, err := gnoland.LoadPackagesFromDir(cfg, examplesDir, creator, defaultFee) require.NoError(t, err) return txs diff --git a/gno.land/pkg/integration/pkgloader.go b/gno.land/pkg/integration/pkgloader.go index 71b1491b2a8..38acbc8b873 100644 --- a/gno.land/pkg/integration/pkgloader.go +++ b/gno.land/pkg/integration/pkgloader.go @@ -7,15 +7,15 @@ import ( "github.com/gnolang/gno/gno.land/pkg/gnoland" "github.com/gnolang/gno/gno.land/pkg/sdk/vm" + "github.com/gnolang/gno/gnovm/pkg/gnoenv" "github.com/gnolang/gno/gnovm/pkg/gnolang" - "github.com/gnolang/gno/gnovm/pkg/gnomod" "github.com/gnolang/gno/gnovm/pkg/packages" "github.com/gnolang/gno/tm2/pkg/crypto" "github.com/gnolang/gno/tm2/pkg/std" ) type PkgsLoader struct { - pkgs []gnomod.Pkg + pkgs []*packages.Package visited map[string]struct{} // list of occurrences to patchs with the given value @@ -25,13 +25,13 @@ type PkgsLoader struct { func NewPkgsLoader() *PkgsLoader { return &PkgsLoader{ - pkgs: make([]gnomod.Pkg, 0), + pkgs: make([]*packages.Package, 0), visited: make(map[string]struct{}), patchs: make(map[string]string), } } -func (pl *PkgsLoader) List() gnomod.PkgList { +func (pl *PkgsLoader) List() packages.PkgList { return pl.pkgs } @@ -49,7 +49,7 @@ func (pl *PkgsLoader) LoadPackages(creatorKey crypto.PrivKey, fee std.Fee, depos for i, pkg := range pkgslist { tx, err := gnoland.LoadPackage(pkg, creatorKey.PubKey().Address(), fee, deposit) if err != nil { - return nil, fmt.Errorf("unable to load pkg %q: %w", pkg.Name, err) + return nil, fmt.Errorf("unable to load pkg %q: %w", pkg.ImportPath, err) } // If any replace value is specified, apply them @@ -86,7 +86,8 @@ func (pl *PkgsLoader) LoadPackages(creatorKey crypto.PrivKey, fee std.Fee, depos func (pl *PkgsLoader) LoadAllPackagesFromDir(path string) error { // list all packages from target path - pkgslist, err := gnomod.ListPkgs(path) + cfg := &packages.LoadConfig{SelfContained: true} + pkgslist, err := packages.Load(cfg, filepath.Join(path, "...")) if err != nil { return fmt.Errorf("listing gno packages: %w", err) } @@ -100,74 +101,36 @@ func (pl *PkgsLoader) LoadAllPackagesFromDir(path string) error { return nil } -func (pl *PkgsLoader) LoadPackage(modroot string, path, name string) error { - // Initialize a queue with the root package - queue := []gnomod.Pkg{{Dir: path, Name: name}} - - for len(queue) > 0 { - // Dequeue the first package - currentPkg := queue[0] - queue = queue[1:] - - if currentPkg.Dir == "" { - return fmt.Errorf("no path specified for package") - } - - if currentPkg.Name == "" { - // Load `gno.mod` information - gnoModPath := filepath.Join(currentPkg.Dir, "gno.mod") - gm, err := gnomod.ParseGnoMod(gnoModPath) - if err != nil { - return fmt.Errorf("unable to load %q: %w", gnoModPath, err) - } - gm.Sanitize() - - // Override package info with mod infos - currentPkg.Name = gm.Module.Mod.Path - currentPkg.Draft = gm.Draft - - pkg, err := gnolang.ReadMemPackage(currentPkg.Dir, currentPkg.Name) - if err != nil { - return fmt.Errorf("unable to read package at %q: %w", currentPkg.Dir, err) - } - importsMap, err := packages.Imports(pkg, nil) - if err != nil { - return fmt.Errorf("unable to load package imports in %q: %w", currentPkg.Dir, err) - } - imports := importsMap.Merge(packages.FileKindPackageSource, packages.FileKindTest, packages.FileKindFiletest) - for _, imp := range imports { - if imp.PkgPath == currentPkg.Name || gnolang.IsStdlib(imp.PkgPath) { - continue - } - currentPkg.Imports = append(currentPkg.Imports, imp.PkgPath) - } - } +func (pl *PkgsLoader) LoadPackage(pkgDir string, name string) error { + examples := filepath.Join(gnoenv.RootDir(), "examples", "...") + cfg := &packages.LoadConfig{Deps: true, SelfContained: true, DepsPatterns: []string{examples}} + pkgs, err := packages.Load(cfg, pkgDir) + if err != nil { + return fmt.Errorf("%q: loading: %w", pkgDir, err) + } - if currentPkg.Draft { - continue // Skip draft package + for _, pkg := range pkgs { + if pkg.Dir == pkgDir && name != "" { + pkg.ImportPath = name } - - if pl.exist(currentPkg) { + if gnolang.IsStdlib(pkg.ImportPath) { continue } - pl.add(currentPkg) - - // Add requirements to the queue - for _, pkgPath := range currentPkg.Imports { - fullPath := filepath.Join(modroot, pkgPath) - queue = append(queue, gnomod.Pkg{Dir: fullPath}) + if pl.exist(pkg) { + continue } + pl.add(pkg) } return nil } -func (pl *PkgsLoader) add(pkg gnomod.Pkg) { - pl.visited[pkg.Name] = struct{}{} +func (pl *PkgsLoader) add(pkg *packages.Package) { + pl.visited[pkg.ImportPath] = struct{}{} pl.pkgs = append(pl.pkgs, pkg) } -func (pl *PkgsLoader) exist(pkg gnomod.Pkg) (ok bool) { - _, ok = pl.visited[pkg.Name] +func (pl *PkgsLoader) exist(pkg *packages.Package) (ok bool) { + _, ok = pl.visited[pkg.ImportPath] return } diff --git a/gno.land/pkg/integration/testscript_gnoland.go b/gno.land/pkg/integration/testscript_gnoland.go index 9781799ea7d..b35f19b8b22 100644 --- a/gno.land/pkg/integration/testscript_gnoland.go +++ b/gno.land/pkg/integration/testscript_gnoland.go @@ -526,7 +526,7 @@ func loadpkgCmd(gnoRootDir string) func(ts *testscript.TestScript, neg bool, arg path = filepath.Join(examplesDir, path) } - if err := pkgs.LoadPackage(examplesDir, path, name); err != nil { + if err := pkgs.LoadPackage(path, name); err != nil { ts.Fatalf("`loadpkg` unable to load package(s) from %q: %s", args[0], err) } diff --git a/gno.land/pkg/sdk/vm/keeper.go b/gno.land/pkg/sdk/vm/keeper.go index bf16cd44243..5b588a8524d 100644 --- a/gno.land/pkg/sdk/vm/keeper.go +++ b/gno.land/pkg/sdk/vm/keeper.go @@ -349,7 +349,7 @@ func (vm *VMKeeper) AddPackage(ctx sdk.Context, msg MsgAddPackage) (err error) { // Validate Gno syntax and type check. format := true - if err := gno.TypeCheckMemPackage(memPkg, gnostore, format); err != nil { + if err := gno.TypeCheckMemPackage("", memPkg, gnostore, format); err != nil { return ErrTypeCheck(err) } @@ -564,7 +564,7 @@ func (vm *VMKeeper) Run(ctx sdk.Context, msg MsgRun) (res string, err error) { // Validate Gno syntax and type check. format := false - if err = gno.TypeCheckMemPackage(memPkg, gnostore, format); err != nil { + if err = gno.TypeCheckMemPackage("", memPkg, gnostore, format); err != nil { return "", ErrTypeCheck(err) } diff --git a/gnovm/cmd/gno/lint.go b/gnovm/cmd/gno/lint.go index ce3465b484e..16c7d552c45 100644 --- a/gnovm/cmd/gno/lint.go +++ b/gnovm/cmd/gno/lint.go @@ -16,15 +16,16 @@ import ( "github.com/gnolang/gno/gnovm" "github.com/gnolang/gno/gnovm/pkg/gnoenv" gno "github.com/gnolang/gno/gnovm/pkg/gnolang" - "github.com/gnolang/gno/gnovm/pkg/gnomod" + "github.com/gnolang/gno/gnovm/pkg/packages" "github.com/gnolang/gno/gnovm/pkg/test" "github.com/gnolang/gno/tm2/pkg/commands" "go.uber.org/multierr" ) type lintCfg struct { - verbose bool - rootDir string + verbose bool + rootDir string + rootExamples bool // min_confidence: minimum confidence of a problem to print it (default 0.8) // auto-fix: apply suggested fixes automatically. } @@ -50,6 +51,7 @@ func (c *lintCfg) RegisterFlags(fs *flag.FlagSet) { fs.BoolVar(&c.verbose, "v", false, "verbose output when lintning") fs.StringVar(&c.rootDir, "root-dir", rootdir, "clone location of github.com/gnolang/gno (gno tries to guess it)") + fs.BoolVar(&c.rootExamples, "root-examples", false, "use the examples present in GNOROOT rather than downloading them") } type lintCode int @@ -73,8 +75,9 @@ type lintIssue struct { } func (i lintIssue) String() string { + location := tryRelativize(i.Location) // TODO: consider crafting a doc URL based on Code. - return fmt.Sprintf("%s: %s (code=%d)", i.Location, i.Msg, i.Code) + return fmt.Sprintf("%s: %s (code=%d)", location, i.Msg, i.Code) } func execLint(cfg *lintCfg, args []string, io commands.IO) error { @@ -90,65 +93,90 @@ func execLint(cfg *lintCfg, args []string, io commands.IO) error { rootDir = gnoenv.RootDir() } - pkgPaths, err := gnoPackagesFromArgsRecursively(args) + loadCfg := &packages.LoadConfig{IO: io, Fetcher: testPackageFetcher} + if cfg.rootExamples { + examples := filepath.Join(gnoenv.RootDir(), "examples", "...") + loadCfg.DepsPatterns = append(loadCfg.DepsPatterns, examples) + } + + pkgs, err := packages.Load(loadCfg, args...) if err != nil { return fmt.Errorf("list packages from args: %w", err) } + pkgsMap := map[string]*packages.Package{} + packages.Inject(pkgsMap, pkgs) + hasError := false bs, ts := test.Store( - rootDir, false, + rootDir, pkgsMap, false, nopReader{}, goio.Discard, goio.Discard, ) - for _, pkgPath := range pkgPaths { - if verbose { - io.ErrPrintln(pkgPath) + for _, pkg := range pkgs { + logName := pkg.ImportPath + if logName == "" { + logName = pkg.Dir } - info, err := os.Stat(pkgPath) - if err == nil && !info.IsDir() { - pkgPath = filepath.Dir(pkgPath) + if verbose { + io.ErrPrintln(logName) } // Check if 'gno.mod' exists - gmFile, err := gnomod.ParseAt(pkgPath) - if err != nil { + if pkg.Root == "" && pkg.ImportPath != "command-line-arguments" { issue := lintIssue{ Code: lintGnoMod, Confidence: 1, - Location: pkgPath, - Msg: err.Error(), + Location: pkg.Dir, + Msg: "gno.mod file not found in current or any parent directory", } io.ErrPrintln(issue) hasError = true } - memPkg, err := gno.ReadMemPackage(pkgPath, pkgPath) + // load deps + loadDepsCfg := *loadCfg + loadDepsCfg.Deps = true + loadDepsCfg.Cache = pkgsMap + deps, loadDepsErr := packages.Load(&loadDepsCfg, pkg.Dir) + if loadDepsErr != nil { + io.ErrPrintln(issueFromError(pkg.Dir, err).String()) + hasError = true + continue + } + packages.Inject(pkgsMap, deps) + + // read mempkg + memPkgPath := pkg.ImportPath + if memPkgPath == "" || memPkgPath == "command-line-arguments" { + memPkgPath = pkg.Dir + } + memPkg, err := gno.ReadMemPackage(pkg.Dir, memPkgPath) if err != nil { - io.ErrPrintln(issueFromError(pkgPath, err).String()) + io.ErrPrintln(issueFromError(pkg.Dir, err).String()) hasError = true continue } // Perform imports using the parent store. if err := test.LoadImports(ts, memPkg); err != nil { - io.ErrPrintln(issueFromError(pkgPath, err).String()) + io.ErrPrintln(issueFromError(pkg.Dir, err).String()) hasError = true continue } // Handle runtime errors - hasRuntimeErr := catchRuntimeError(pkgPath, io.Err(), func() { + hasRuntimeErr := catchRuntimeError(pkg.Dir, io.Err(), func() { // Wrap in cache wrap so execution of the linter doesn't impact // other packages. cw := bs.CacheWrap() gs := ts.BeginTransaction(cw, cw, nil) // Run type checking - if gmFile == nil || !gmFile.Draft { - foundErr, err := lintTypeCheck(io, memPkg, gs) + if !pkg.Draft { + foundErr, err := lintTypeCheck(io, pkg.Dir, memPkg, gs) if err != nil { io.ErrPrintln(err) hasError = true @@ -156,7 +184,7 @@ func execLint(cfg *lintCfg, args []string, io commands.IO) error { hasError = true } } else if verbose { - io.ErrPrintfln("%s: module is draft, skipping type check", pkgPath) + io.ErrPrintfln("%s: module is draft, skipping type check", logName) } tm := test.Machine(gs, goio.Discard, memPkg.Path) @@ -182,8 +210,8 @@ func execLint(cfg *lintCfg, args []string, io commands.IO) error { return nil } -func lintTypeCheck(io commands.IO, memPkg *gnovm.MemPackage, testStore gno.Store) (errorsFound bool, err error) { - tcErr := gno.TypeCheckMemPackageTest(memPkg, testStore) +func lintTypeCheck(io commands.IO, pkgDir string, memPkg *gnovm.MemPackage, testStore gno.Store) (errorsFound bool, err error) { + tcErr := gno.TypeCheckMemPackageTest(pkgDir, memPkg, testStore) if tcErr == nil { return false, nil } @@ -242,12 +270,12 @@ func lintTestFiles(memPkg *gnovm.MemPackage) *gno.FileSet { return testfiles } -func guessSourcePath(pkg, source string) string { - if info, err := os.Stat(pkg); !os.IsNotExist(err) && !info.IsDir() { - pkg = filepath.Dir(pkg) +func guessSourcePath(pkgDir, source string) string { + if info, err := os.Stat(pkgDir); !os.IsNotExist(err) && !info.IsDir() { + pkgDir = filepath.Dir(pkgDir) } - sourceJoin := filepath.Join(pkg, source) + sourceJoin := filepath.Join(pkgDir, source) if _, err := os.Stat(sourceJoin); !os.IsNotExist(err) { return filepath.Clean(sourceJoin) } @@ -256,7 +284,7 @@ func guessSourcePath(pkg, source string) string { return filepath.Clean(source) } - return filepath.Clean(pkg) + return filepath.Clean(pkgDir) } // reParseRecover is a regex designed to parse error details from a string. @@ -264,7 +292,7 @@ func guessSourcePath(pkg, source string) string { // XXX: Ideally, error handling should encapsulate location details within a dedicated error type. var reParseRecover = regexp.MustCompile(`^([^:]+)((?::(?:\d+)){1,2}):? *(.*)$`) -func catchRuntimeError(pkgPath string, stderr goio.WriteCloser, action func()) (hasError bool) { +func catchRuntimeError(pkgDir string, stderr goio.WriteCloser, action func()) (hasError bool) { defer func() { // Errors catched here mostly come from: gnovm/pkg/gnolang/preprocess.go r := recover() @@ -275,21 +303,21 @@ func catchRuntimeError(pkgPath string, stderr goio.WriteCloser, action func()) ( switch verr := r.(type) { case *gno.PreprocessError: err := verr.Unwrap() - fmt.Fprintln(stderr, issueFromError(pkgPath, err).String()) + fmt.Fprintln(stderr, issueFromError(pkgDir, err).String()) case error: errors := multierr.Errors(verr) for _, err := range errors { errList, ok := err.(scanner.ErrorList) if ok { for _, errorInList := range errList { - fmt.Fprintln(stderr, issueFromError(pkgPath, errorInList).String()) + fmt.Fprintln(stderr, issueFromError(pkgDir, errorInList).String()) } } else { - fmt.Fprintln(stderr, issueFromError(pkgPath, err).String()) + fmt.Fprintln(stderr, issueFromError(pkgDir, err).String()) } } case string: - fmt.Fprintln(stderr, issueFromError(pkgPath, errors.New(verr)).String()) + fmt.Fprintln(stderr, issueFromError(pkgDir, errors.New(verr)).String()) default: panic(r) } @@ -299,21 +327,21 @@ func catchRuntimeError(pkgPath string, stderr goio.WriteCloser, action func()) ( return } -func issueFromError(pkgPath string, err error) lintIssue { +func issueFromError(pkgDir string, err error) lintIssue { var issue lintIssue issue.Confidence = 1 issue.Code = lintGnoError parsedError := strings.TrimSpace(err.Error()) - parsedError = strings.TrimPrefix(parsedError, pkgPath+"/") + parsedError = strings.TrimPrefix(parsedError, pkgDir+"/") matches := reParseRecover.FindStringSubmatch(parsedError) if len(matches) > 0 { - sourcepath := guessSourcePath(pkgPath, matches[1]) + sourcepath := guessSourcePath(pkgDir, matches[1]) issue.Location = sourcepath + matches[2] issue.Msg = strings.TrimSpace(matches[3]) } else { - issue.Location = fmt.Sprintf("%s:0", filepath.Clean(pkgPath)) + issue.Location = fmt.Sprintf("%s:0", filepath.Clean(pkgDir)) issue.Msg = err.Error() } return issue diff --git a/gnovm/cmd/gno/lint_test.go b/gnovm/cmd/gno/lint_test.go index 4589fc55f92..72ca51f240e 100644 --- a/gnovm/cmd/gno/lint_test.go +++ b/gnovm/cmd/gno/lint_test.go @@ -12,15 +12,15 @@ func TestLintApp(t *testing.T) { errShouldBe: "flag: help requested", }, { args: []string{"lint", "../../tests/integ/run_main/"}, - stderrShouldContain: "./../../tests/integ/run_main: gno.mod file not found in current or any parent directory (code=1)", + stderrShouldContain: "../../tests/integ/run_main: gno.mod file not found in current or any parent directory (code=1)", errShouldBe: "exit code: 1", }, { args: []string{"lint", "../../tests/integ/undefined_variable_test/undefined_variables_test.gno"}, - stderrShouldContain: "undefined_variables_test.gno:6:28: name toto not declared (code=2)", + stderrShouldContain: "../../tests/integ/undefined_variable_test/undefined_variables_test.gno:6:28: name toto not declared (code=2)", errShouldBe: "exit code: 1", }, { args: []string{"lint", "../../tests/integ/package_not_declared/main.gno"}, - stderrShouldContain: "main.gno:4:2: name fmt not declared (code=2)", + stderrShouldContain: "../../tests/integ/package_not_declared/main.gno:4:2: undefined: fmt (code=4)", errShouldBe: "exit code: 1", }, { args: []string{"lint", "../../tests/integ/several-lint-errors/main.gno"}, diff --git a/gnovm/cmd/gno/list.go b/gnovm/cmd/gno/list.go new file mode 100644 index 00000000000..ef737a00b64 --- /dev/null +++ b/gnovm/cmd/gno/list.go @@ -0,0 +1,88 @@ +package main + +import ( + "context" + "encoding/json" + "flag" + "fmt" + "os" + "strings" + + "github.com/gnolang/gno/gnovm/pkg/packages" + "github.com/gnolang/gno/tm2/pkg/commands" +) + +type listCfg struct { + json bool + deps bool +} + +func newListCmd(io commands.IO) *commands.Command { + cfg := &listCfg{} + + return commands.NewCommand( + commands.Metadata{ + Name: "list", + ShortUsage: "list [flags] [...]", + ShortHelp: "runs the lister for the specified packages", + }, + cfg, + func(_ context.Context, args []string) error { + return execList(cfg, args, io) + }, + ) +} + +func (c *listCfg) RegisterFlags(fs *flag.FlagSet) { + fs.BoolVar(&c.json, "json", false, `The -json flag causes the package data to be printed in JSON format +instead of using the template format. The JSON flag can optionally be +provided with a set of comma-separated required field names to be output. +If so, those required fields will always appear in JSON output, but +others may be omitted to save work in computing the JSON struct.`) + fs.BoolVar(&c.deps, "deps", false, `The -deps flag causes list to iterate over not just the named packages +but also all their dependencies. It visits them in a depth-first post-order +traversal, so that a package is listed only after all its dependencies. +Packages not explicitly listed on the command line will have the DepOnly +field set to true`) +} + +func execList(cfg *listCfg, args []string, io commands.IO) error { + if len(args) < 1 { + return flag.ErrHelp + } + + conf := &packages.LoadConfig{IO: io, Fetcher: testPackageFetcher} + + if cfg.deps { + conf.Deps = true + } + + if !cfg.json { + pkgs, err := packages.Load(conf, args...) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + pkgPaths := make([]string, len(pkgs)) + for i, pkg := range pkgs { + pkgPaths[i] = pkg.ImportPath + } + fmt.Println(strings.Join(pkgPaths, "\n")) + return nil + } + + pkgs, err := packages.Load(conf, args...) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + for _, pkg := range pkgs { + pkgBytes, err := json.MarshalIndent(pkg, "", "\t") + if err != nil { + panic(err) + } + fmt.Println(string(pkgBytes)) + } + + return nil +} diff --git a/gnovm/cmd/gno/main.go b/gnovm/cmd/gno/main.go index 7a5799f2835..c951fa2c9f3 100644 --- a/gnovm/cmd/gno/main.go +++ b/gnovm/cmd/gno/main.go @@ -34,6 +34,7 @@ func newGnocliCmd(io commands.IO) *commands.Command { newDocCmd(io), newEnvCmd(io), newBugCmd(io), + newListCmd(io), newFmtCmd(io), // graph // vendor -- download deps from the chain in vendor/ diff --git a/gnovm/cmd/gno/main_test.go b/gnovm/cmd/gno/main_test.go index 2ea3e31f977..c4ea1c7394a 100644 --- a/gnovm/cmd/gno/main_test.go +++ b/gnovm/cmd/gno/main_test.go @@ -9,7 +9,7 @@ import ( "strings" "testing" - "github.com/gnolang/gno/gnovm/cmd/gno/internal/pkgdownload/examplespkgfetcher" + "github.com/gnolang/gno/gnovm/pkg/packages/pkgdownload/examplespkgfetcher" "github.com/gnolang/gno/tm2/pkg/commands" "github.com/stretchr/testify/require" ) diff --git a/gnovm/cmd/gno/mod.go b/gnovm/cmd/gno/mod.go index 5479d934ce6..b704870cba7 100644 --- a/gnovm/cmd/gno/mod.go +++ b/gnovm/cmd/gno/mod.go @@ -4,14 +4,16 @@ import ( "context" "flag" "fmt" + "go/token" "os" "path/filepath" + "strconv" "strings" - "github.com/gnolang/gno/gnovm/cmd/gno/internal/pkgdownload" - "github.com/gnolang/gno/gnovm/cmd/gno/internal/pkgdownload/rpcpkgfetcher" "github.com/gnolang/gno/gnovm/pkg/gnomod" "github.com/gnolang/gno/gnovm/pkg/packages" + "github.com/gnolang/gno/gnovm/pkg/packages/pkgdownload" + "github.com/gnolang/gno/gnovm/pkg/packages/pkgdownload/rpcpkgfetcher" "github.com/gnolang/gno/tm2/pkg/commands" "github.com/gnolang/gno/tm2/pkg/errors" "go.uber.org/multierr" @@ -184,7 +186,8 @@ func execModDownload(cfg *modDownloadCfg, args []string, io commands.IO) error { return fmt.Errorf("validate: %w", err) } - if err := downloadDeps(io, path, gnoMod, fetcher); err != nil { + conf := &packages.LoadConfig{IO: io, Fetcher: fetcher} + if err := packages.DownloadDeps(conf, path, gnoMod); err != nil { return err } @@ -256,7 +259,8 @@ func execModTidy(cfg *modTidyCfg, args []string, io commands.IO) error { } if cfg.recursive { - pkgs, err := gnomod.ListPkgs(wd) + loadCfg := packages.LoadConfig{IO: io, Fetcher: testPackageFetcher} + pkgs, err := packages.Load(&loadCfg, filepath.Join(wd, "...")) if err != nil { return err } @@ -296,23 +300,27 @@ func execModWhy(args []string, io commands.IO) error { return flag.ErrHelp } - wd, err := os.Getwd() + conf := &packages.LoadConfig{SelfContained: true, Fetcher: testPackageFetcher} + pkgs, err := packages.Load(conf, ".") if err != nil { return err } - fname := filepath.Join(wd, "gno.mod") - gm, err := gnomod.ParseGnoMod(fname) - if err != nil { - return err + if len(pkgs) < 1 { + return errors.New("no package found") } + pkg := pkgs[0] - importToFilesMap, err := getImportToFilesMap(wd) + importToFilesMap, err := getImportToFilesMap(pkg) if err != nil { return err } + if pkg.ModPath == "" { + pkg.ModPath = "." + } + // Format and print `gno mod why` output stanzas - out := formatModWhyStanzas(gm.Module.Mod.Path, args, importToFilesMap) + out := formatModWhyStanzas(pkg.ModPath, args, importToFilesMap) io.Printf(out) return nil @@ -343,11 +351,12 @@ func formatModWhyStanzas(modulePath string, args []string, importToFilesMap map[ // getImportToFilesMap returns a map where each key is an import path and its // value is a list of files importing that package with the specified import path. -func getImportToFilesMap(pkgPath string) (map[string][]string, error) { - entries, err := os.ReadDir(pkgPath) +func getImportToFilesMap(pkg *packages.Package) (map[string][]string, error) { + entries, err := os.ReadDir(pkg.Dir) if err != nil { return nil, err } + fset := token.NewFileSet() m := make(map[string][]string) // import -> []file for _, e := range entries { filename := e.Name() @@ -358,17 +367,23 @@ func getImportToFilesMap(pkgPath string) (map[string][]string, error) { continue } - data, err := os.ReadFile(filepath.Join(pkgPath, filename)) + data, err := os.ReadFile(filepath.Join(pkg.Dir, filename)) if err != nil { return nil, err } - imports, err := packages.FileImports(filename, string(data), nil) + imports, err := packages.FileImportsSpecs(filename, string(data), fset) if err != nil { return nil, err } for _, imp := range imports { - m[imp.PkgPath] = append(m[imp.PkgPath], filename) + pkgPath, err := strconv.Unquote(imp.Path.Value) + if err != nil { + // should not happen - parser.ParseFile should already ensure we get + // a valid string literal here. + panic(fmt.Errorf("%v: unexpected invalid import path: %v", fset.Position(imp.Pos()).String(), imp.Path.Value)) + } + m[pkgPath] = append(m[pkgPath], filename) } } return m, nil diff --git a/gnovm/cmd/gno/mod_test.go b/gnovm/cmd/gno/mod_test.go index afce25597cd..efd60d2906e 100644 --- a/gnovm/cmd/gno/mod_test.go +++ b/gnovm/cmd/gno/mod_test.go @@ -177,7 +177,9 @@ func TestModApp(t *testing.T) { args: []string{"mod", "why", "std"}, testDir: "../../tests/integ/empty_dir", simulateExternalRepo: true, - errShouldContain: "could not read gno.mod file", + stdoutShouldBe: `# std +(module . does not need package std) +`, }, { args: []string{"mod", "why", "std"}, diff --git a/gnovm/cmd/gno/run.go b/gnovm/cmd/gno/run.go index 9a9beac5cd1..79d719680d2 100644 --- a/gnovm/cmd/gno/run.go +++ b/gnovm/cmd/gno/run.go @@ -6,11 +6,9 @@ import ( "flag" "fmt" "io" - "os" - "path/filepath" - "strings" "github.com/gnolang/gno/gnovm/pkg/gnoenv" + "github.com/gnolang/gno/gnovm/pkg/gnofiles" gno "github.com/gnolang/gno/gnovm/pkg/gnolang" "github.com/gnolang/gno/gnovm/pkg/test" "github.com/gnolang/gno/tm2/pkg/commands" @@ -93,7 +91,7 @@ func execRun(cfg *runCfg, args []string, io commands.IO) error { // init store and machine _, testStore := test.Store( - cfg.rootDir, false, + cfg.rootDir, nil, false, stdin, stdout, stderr) if cfg.verbose { testStore.SetLogStoreOps(true) @@ -142,54 +140,24 @@ func execRun(cfg *runCfg, args []string, io commands.IO) error { } func parseFiles(fnames []string, stderr io.WriteCloser) ([]*gno.FileNode, error) { - files := make([]*gno.FileNode, 0, len(fnames)) - var hasError bool - for _, fname := range fnames { - if s, err := os.Stat(fname); err == nil && s.IsDir() { - subFns, err := listNonTestFiles(fname) - if err != nil { - return nil, err - } - subFiles, err := parseFiles(subFns, stderr) - if err != nil { - return nil, err - } - files = append(files, subFiles...) - continue - } else if err != nil { - // either not found or some other kind of error -- - // in either case not a file we can parse. - return nil, err - } + gnoFnames, err := gnofiles.Match(fnames, gnofiles.MatchFiles("!*_test.gno", "!*_filetest.gno")) + if err != nil { + return nil, err + } + var hasError bool + files := make([]*gno.FileNode, 0, len(gnoFnames)) + for _, fname := range gnoFnames { hasError = catchRuntimeError(fname, stderr, func() { files = append(files, gno.MustReadFile(fname)) - }) + }) || hasError } - if hasError { return nil, commands.ExitCodeError(1) } return files, nil } -func listNonTestFiles(dir string) ([]string, error) { - fs, err := os.ReadDir(dir) - if err != nil { - return nil, err - } - fn := make([]string, 0, len(fs)) - for _, f := range fs { - n := f.Name() - if isGnoFile(f) && - !strings.HasSuffix(n, "_test.gno") && - !strings.HasSuffix(n, "_filetest.gno") { - fn = append(fn, filepath.Join(dir, n)) - } - } - return fn, nil -} - func runExpr(m *gno.Machine, expr string) { defer func() { if r := recover(); r != nil { diff --git a/gnovm/cmd/gno/test.go b/gnovm/cmd/gno/test.go index fec0de7c221..a5e0eca193b 100644 --- a/gnovm/cmd/gno/test.go +++ b/gnovm/cmd/gno/test.go @@ -5,17 +5,13 @@ import ( "flag" "fmt" goio "io" - "log" - "path/filepath" - "strings" "time" "github.com/gnolang/gno/gnovm/pkg/gnoenv" gno "github.com/gnolang/gno/gnovm/pkg/gnolang" - "github.com/gnolang/gno/gnovm/pkg/gnomod" + "github.com/gnolang/gno/gnovm/pkg/packages" "github.com/gnolang/gno/gnovm/pkg/test" "github.com/gnolang/gno/tm2/pkg/commands" - "github.com/gnolang/gno/tm2/pkg/random" ) type testCfg struct { @@ -155,15 +151,20 @@ func execTest(cfg *testCfg, args []string, io commands.IO) error { cfg.rootDir = gnoenv.RootDir() } - paths, err := targetsFromPatterns(args) - if err != nil { - return fmt.Errorf("list targets from patterns: %w", err) + // Find targets for test. + conf := &packages.LoadConfig{ + IO: io, + Fetcher: testPackageFetcher, + DepsPatterns: []string{"./..."}, } - if len(paths) == 0 { - io.ErrPrintln("no packages to test") - return nil + pkgs, err := packages.Load(conf, args...) + if err != nil { + return err } + pkgsMap := map[string]*packages.Package{} + packages.Inject(pkgsMap, pkgs) + if cfg.timeout > 0 { go func() { time.Sleep(cfg.timeout) @@ -171,17 +172,12 @@ func execTest(cfg *testCfg, args []string, io commands.IO) error { }() } - subPkgs, err := gnomod.SubPkgsFromPaths(paths) - if err != nil { - return fmt.Errorf("list sub packages: %w", err) - } - // Set up options to run tests. stdout := goio.Discard if cfg.verbose { stdout = io.Out() } - opts := test.NewTestOptions(cfg.rootDir, io.In(), stdout, io.Err()) + opts := test.NewTestOptions(cfg.rootDir, pkgsMap, io.In(), stdout, io.Err()) opts.RunFlag = cfg.run opts.Sync = cfg.updateGoldenTests opts.Verbose = cfg.verbose @@ -190,29 +186,47 @@ func execTest(cfg *testCfg, args []string, io commands.IO) error { buildErrCount := 0 testErrCount := 0 - for _, pkg := range subPkgs { - if len(pkg.TestGnoFiles) == 0 && len(pkg.FiletestGnoFiles) == 0 { - io.ErrPrintfln("? %s \t[no test files]", pkg.Dir) + for _, pkg := range pkgs { + // ignore deps + if len(pkg.Match) == 0 { continue } - // Determine gnoPkgPath by reading gno.mod - var gnoPkgPath string - modfile, err := gnomod.ParseAt(pkg.Dir) - if err == nil { - gnoPkgPath = modfile.Module.Mod.Path - } else { - gnoPkgPath = pkgPathFromRootDir(pkg.Dir, cfg.rootDir) - if gnoPkgPath == "" { - // unable to read pkgPath from gno.mod, generate a random realm path - io.ErrPrintfln("--- WARNING: unable to read package path from gno.mod or gno root directory; try creating a gno.mod file") - gnoPkgPath = "gno.land/r/" + strings.ToLower(random.RandStr(8)) // XXX: gno.land hardcoded for convenience. - } + + if !pkg.Draft && pkg.Files.Size() == 0 { + return fmt.Errorf("no Gno files in %s", pkg.Dir) + } + + label := pkg.ImportPath + if label == "" { + label = pkg.Dir + } + label = tryRelativize(label) + + if len(pkg.Files[packages.FileKindTest]) == 0 && len(pkg.Files[packages.FileKindXTest]) == 0 && len(pkg.Files[packages.FileKindFiletest]) == 0 { + io.ErrPrintfln("? %s \t[no test files]", label) + continue } - memPkg := gno.MustReadMemPackage(pkg.Dir, gnoPkgPath) + depsConf := *conf + depsConf.Deps = true + depsConf.Cache = pkgsMap + deps, loadDepsErr := packages.Load(&depsConf, pkg.Dir) + if loadDepsErr != nil { + io.ErrPrintfln("%s: load deps: %v", label, err) + buildErrCount++ + continue + } + packages.Inject(pkgsMap, deps) + + memPkg, err := gno.ReadMemPackage(pkg.Dir, label) + if err != nil { + io.ErrPrintln(err) + buildErrCount++ + continue + } startedAt := time.Now() - hasError := catchRuntimeError(gnoPkgPath, io.Err(), func() { + hasError := catchRuntimeError(pkg.Dir, io.Err(), func() { err = test.Test(memPkg, pkg.Dir, opts) }) @@ -221,14 +235,14 @@ func execTest(cfg *testCfg, args []string, io commands.IO) error { if hasError || err != nil { if err != nil { - io.ErrPrintfln("%s: test pkg: %v", pkg.Dir, err) + io.ErrPrintfln("%s: test pkg: %v", label, err) } io.ErrPrintfln("FAIL") - io.ErrPrintfln("FAIL %s \t%s", pkg.Dir, dstr) + io.ErrPrintfln("FAIL %s \t%s", label, dstr) io.ErrPrintfln("FAIL") testErrCount++ } else { - io.ErrPrintfln("ok %s \t%s", pkg.Dir, dstr) + io.ErrPrintfln("ok %s \t%s", label, dstr) } } if testErrCount > 0 || buildErrCount > 0 { @@ -238,32 +252,3 @@ func execTest(cfg *testCfg, args []string, io commands.IO) error { return nil } - -// attempts to determine the full gno pkg path by analyzing the directory. -func pkgPathFromRootDir(pkgPath, rootDir string) string { - abPkgPath, err := filepath.Abs(pkgPath) - if err != nil { - log.Printf("could not determine abs path: %v", err) - return "" - } - abRootDir, err := filepath.Abs(rootDir) - if err != nil { - log.Printf("could not determine abs path: %v", err) - return "" - } - abRootDir += string(filepath.Separator) - if !strings.HasPrefix(abPkgPath, abRootDir) { - return "" - } - impPath := strings.ReplaceAll(abPkgPath[len(abRootDir):], string(filepath.Separator), "/") - for _, prefix := range [...]string{ - "examples/", - "gnovm/stdlibs/", - "gnovm/tests/stdlibs/", - } { - if strings.HasPrefix(impPath, prefix) { - return impPath[len(prefix):] - } - } - return "" -} diff --git a/gnovm/cmd/gno/testdata/lint/no_gnomod.txtar b/gnovm/cmd/gno/testdata/lint/no_gnomod.txtar index b5a046a7095..072e7cca551 100644 --- a/gnovm/cmd/gno/testdata/lint/no_gnomod.txtar +++ b/gnovm/cmd/gno/testdata/lint/no_gnomod.txtar @@ -14,4 +14,4 @@ func main() { -- stdout.golden -- -- stderr.golden -- -./.: parsing gno.mod at ./.: gno.mod file not found in current or any parent directory (code=1) +.: gno.mod file not found in current or any parent directory (code=1) diff --git a/gnovm/cmd/gno/testdata/test/empty_dir.txtar b/gnovm/cmd/gno/testdata/test/empty_dir.txtar index ffed64ab9c7..f7ea441e0c3 100644 --- a/gnovm/cmd/gno/testdata/test/empty_dir.txtar +++ b/gnovm/cmd/gno/testdata/test/empty_dir.txtar @@ -1,11 +1,11 @@ # Run gno test on an empty dir -gno test . +! gno test . ! stdout .+ -stderr '[no test files]' +stderr 'no Gno files in ' -gno test ./... +! gno test ./... ! stdout .+ -stderr 'no packages to test' +stderr 'gno: warning: "./..." matched no packages\nno packages' diff --git a/gnovm/cmd/gno/testdata/test/multitest_events.txtar b/gnovm/cmd/gno/testdata/test/multitest_events.txtar index 321c790561a..a7367e29494 100644 --- a/gnovm/cmd/gno/testdata/test/multitest_events.txtar +++ b/gnovm/cmd/gno/testdata/test/multitest_events.txtar @@ -5,7 +5,10 @@ gno test -print-events . ! stdout .+ stderr 'EVENTS: \[{\"type\":\"EventA\",\"attrs\":\[\],\"pkg_path\":\"gno.land/r/.*\",\"func\":\"TestA\"}\]' stderr 'EVENTS: \[{\"type\":\"EventB\",\"attrs\":\[{\"key\":\"keyA\",\"value\":\"valA\"}\],\"pkg_path\":\"gno.land/r/.*\",\"func\":\"TestB\"},{\"type\":\"EventC\",\"attrs\":\[{\"key\":\"keyD\",\"value\":\"valD\"}\],\"pkg_path\":\"gno.land/r/.*\",\"func\":\"TestB\"}\]' -stderr 'ok \. \d\.\d\ds' +stderr 'ok gno.land/r/.* \d\.\d\ds' + +-- gno.mod -- +module gno.land/r/tests/multitest_events -- valid.gno -- package valid diff --git a/gnovm/cmd/gno/testdata/test/realm_boundmethod.txtar b/gnovm/cmd/gno/testdata/test/realm_boundmethod.txtar index 9d935df74c2..e53cde13667 100644 --- a/gnovm/cmd/gno/testdata/test/realm_boundmethod.txtar +++ b/gnovm/cmd/gno/testdata/test/realm_boundmethod.txtar @@ -25,6 +25,9 @@ func (c *Counter) Inc() { c.n++ } +-- examples/gno.land/r/demo/realm1/gno.mod -- +module gno.land/r/demo/realm1 + -- examples/gno.land/r/demo/realm1/realm1.gno -- package realm1 @@ -36,6 +39,9 @@ func GetCounter() *counter.Counter { return &c } +-- examples/gno.land/r/demo/realm2/gno.mod -- +module gno.land/r/demo/realm2 + -- examples/gno.land/r/demo/realm2/realm2.gno -- package realm2 diff --git a/gnovm/cmd/gno/transpile.go b/gnovm/cmd/gno/transpile.go index 1e3081ca2b0..d43f31e3846 100644 --- a/gnovm/cmd/gno/transpile.go +++ b/gnovm/cmd/gno/transpile.go @@ -8,6 +8,7 @@ import ( "go/ast" "go/scanner" "go/token" + "io/fs" "os" "os/exec" "path/filepath" @@ -410,3 +411,76 @@ func parseGoBuildErrors(out string) error { return errList.Err() } + +func gnoFilesFromArgsRecursively(args []string) ([]string, error) { + var paths []string + + for _, argPath := range args { + info, err := os.Stat(argPath) + if err != nil { + return nil, fmt.Errorf("invalid file or package path: %w", err) + } + + if !info.IsDir() { + if isGnoFile(fs.FileInfoToDirEntry(info)) { + paths = append(paths, ensurePathPrefix(argPath)) + } + + continue + } + + // Gather package paths from the directory + err = walkDirForGnoFiles(argPath, func(path string) { + paths = append(paths, ensurePathPrefix(path)) + }) + if err != nil { + return nil, fmt.Errorf("unable to walk dir: %w", err) + } + } + + return paths, nil +} + +func listNonTestFiles(dir string) ([]string, error) { + fs, err := os.ReadDir(dir) + if err != nil { + return nil, err + } + fn := make([]string, 0, len(fs)) + for _, f := range fs { + n := f.Name() + if isGnoFile(f) && + !strings.HasSuffix(n, "_test.gno") && + !strings.HasSuffix(n, "_filetest.gno") { + fn = append(fn, filepath.Join(dir, n)) + } + } + return fn, nil +} + +func walkDirForGnoFiles(root string, addPath func(path string)) error { + visited := make(map[string]struct{}) + + walkFn := func(currPath string, f fs.DirEntry, err error) error { + if err != nil { + return fmt.Errorf("%s: walk dir: %w", root, err) + } + + if f.IsDir() || !isGnoFile(f) { + return nil + } + + parentDir := filepath.Dir(currPath) + if _, found := visited[parentDir]; found { + return nil + } + + visited[parentDir] = struct{}{} + + addPath(parentDir) + + return nil + } + + return filepath.WalkDir(root, walkFn) +} diff --git a/gnovm/cmd/gno/util.go b/gnovm/cmd/gno/util.go index 697aa94b3c6..87f37f14bb7 100644 --- a/gnovm/cmd/gno/util.go +++ b/gnovm/cmd/gno/util.go @@ -21,35 +21,6 @@ func isFileExist(path string) bool { return err == nil } -func gnoFilesFromArgsRecursively(args []string) ([]string, error) { - var paths []string - - for _, argPath := range args { - info, err := os.Stat(argPath) - if err != nil { - return nil, fmt.Errorf("invalid file or package path: %w", err) - } - - if !info.IsDir() { - if isGnoFile(fs.FileInfoToDirEntry(info)) { - paths = append(paths, ensurePathPrefix(argPath)) - } - - continue - } - - // Gather package paths from the directory - err = walkDirForGnoFiles(argPath, func(path string) { - paths = append(paths, ensurePathPrefix(path)) - }) - if err != nil { - return nil, fmt.Errorf("unable to walk dir: %w", err) - } - } - - return paths, nil -} - func gnoFilesFromArgs(args []string) ([]string, error) { var paths []string @@ -92,60 +63,6 @@ func ensurePathPrefix(path string) string { return "." + string(filepath.Separator) + path } -func walkDirForGnoFiles(root string, addPath func(path string)) error { - visited := make(map[string]struct{}) - - walkFn := func(currPath string, f fs.DirEntry, err error) error { - if err != nil { - return fmt.Errorf("%s: walk dir: %w", root, err) - } - - if f.IsDir() || !isGnoFile(f) { - return nil - } - - parentDir := filepath.Dir(currPath) - if _, found := visited[parentDir]; found { - return nil - } - - visited[parentDir] = struct{}{} - - addPath(parentDir) - - return nil - } - - return filepath.WalkDir(root, walkFn) -} - -func gnoPackagesFromArgsRecursively(args []string) ([]string, error) { - var paths []string - - for _, argPath := range args { - info, err := os.Stat(argPath) - if err != nil { - return nil, fmt.Errorf("invalid file or package path: %w", err) - } - - if !info.IsDir() { - paths = append(paths, ensurePathPrefix(argPath)) - - continue - } - - // Gather package paths from the directory - err = walkDirForGnoFiles(argPath, func(path string) { - paths = append(paths, ensurePathPrefix(path)) - }) - if err != nil { - return nil, fmt.Errorf("unable to walk dir: %w", err) - } - } - - return paths, nil -} - // targetsFromPatterns returns a list of target paths that match the patterns. // Each pattern can represent a file or a directory, and if the pattern // includes "/...", the "..." is treated as a wildcard, matching any string. @@ -338,3 +255,23 @@ func copyFile(src, dst string) error { return nil } + +func tryRelativize(dirPath string) string { + rel, err := relativize(dirPath) + if err != nil { + return dirPath + } + return rel +} + +func relativize(dirPath string) (string, error) { + wd, err := os.Getwd() + if err != nil { + return "", err + } + rel, err := filepath.Rel(wd, dirPath) + if err != nil { + return "", err + } + return rel, nil +} diff --git a/gnovm/pkg/doc/dirs.go b/gnovm/pkg/doc/dirs.go index 4c481324e9a..b0761e50c23 100644 --- a/gnovm/pkg/doc/dirs.go +++ b/gnovm/pkg/doc/dirs.go @@ -110,11 +110,7 @@ func packageImportsRecursive(root string, pkgPath string) []string { // ignore packages with invalid imports importsMap = nil } - resRaw := importsMap.Merge(packages.FileKindPackageSource, packages.FileKindTest, packages.FileKindXTest) - res := make([]string, len(resRaw)) - for idx, imp := range resRaw { - res[idx] = imp.PkgPath - } + res := importsMap.Merge(packages.FileKindPackageSource, packages.FileKindTest, packages.FileKindXTest) entries, err := os.ReadDir(root) if err != nil { @@ -132,7 +128,7 @@ func packageImportsRecursive(root string, pkgPath string) []string { for _, imp := range sub { if !slices.Contains(res, imp) { - res = append(res, imp) //nolint:makezero + res = append(res, imp) } } } diff --git a/gnovm/pkg/doc/pkg.go b/gnovm/pkg/doc/pkg.go index 71e1a50f299..618af5af708 100644 --- a/gnovm/pkg/doc/pkg.go +++ b/gnovm/pkg/doc/pkg.go @@ -210,14 +210,14 @@ func (pkg *pkgData) docPackage(opts *WriteDocumentationOptions) (*ast.Package, * // Compute package documentation. // Assign to blank to ignore errors that can happen due to unresolved identifiers. - astpkg, _ := ast.NewPackage(pkg.fset, fileMap, simpleImporter, nil) + astpkg, _ := ast.NewPackage(pkg.fset, fileMap, simplepackages, nil) p := doc.New(astpkg, pkg.dir.importPath, mode) // TODO: classifyExamples(p, Examples(testGoFiles...)) return astpkg, p, nil } -func simpleImporter(imports map[string]*ast.Object, path string) (*ast.Object, error) { +func simplepackages(imports map[string]*ast.Object, path string) (*ast.Object, error) { pkg := imports[path] if pkg == nil { // note that strings.LastIndex returns -1 if there is no "/" diff --git a/gnovm/pkg/gnofiles/gnofiles.go b/gnovm/pkg/gnofiles/gnofiles.go new file mode 100644 index 00000000000..0e3fd2d277b --- /dev/null +++ b/gnovm/pkg/gnofiles/gnofiles.go @@ -0,0 +1,78 @@ +package gnofiles + +import ( + "os" + "path/filepath" +) + +// This file contains "definitions"; it attempts to centralize some common +// answers to common questions like "Is this a gno file?", "What is the import +// path to the gno repository?", "Is this import path of a realm?". + +const ( + // RepoImport is the import path to the Gno repository. + RepoImport = "github.com/gnolang/gno" + + // GnolangImport is the import path to the gnolang package. + GnolangImport = RepoImport + "/gnovm/pkg/gnolang" + + // ModfileName is the name of the module file. + ModfileName = "gno.mod" + + // WorkfileName is the name of the workspace file. + WorkfileName = "gno.work" + + // RecursiveSuffix is the os-dependent suffix marking a recursive target + RecursiveSuffix = string(os.PathSeparator) + "..." +) + +// IsGnoFile determines whether the given files matches all of the given patterns, +// with the same matching rules as [MatchPatterns]. +// +// It is essentially a helper for MatchPatterns, implicitly adding the patterns +// "*.gno" and "!.*". +// +// IsGnoFile assumes its patterns to be syntactically well-formed; if not, it +// will panic. To test for the correctness of patterns, try passing them with +// any input to MatchPatterns. +func IsGnoFile(name string, patterns ...string) bool { + m, err := MatchPatterns(name, append(patterns, "*.gno", "!.*")...) + if err != nil { + panic(err) + } + return m +} + +func IsGnoTestFile(p string) bool { + return IsGnoFile(p, "*_test.gno") +} + +func IsGnoFiletestFile(p string) bool { + return IsGnoFile(p, "*_filetest.gno") +} + +func FindModuleRoot(dir string) (string, error) { + return findRoot(dir, ModfileName) +} + +func FindWorkspaceRoot(dir string) (string, error) { + return findRoot(dir, WorkfileName) +} + +func findRoot(dir string, filename string) (string, error) { + dir = filepath.Clean(dir) + + potentialMod := filepath.Join(dir, filename) + + if _, err := os.Stat(potentialMod); os.IsNotExist(err) { + parent, file := filepath.Split(dir) + if file == "" || (parent == "" && file == ".") { + return "", os.ErrNotExist + } + return findRoot(parent, filename) + } else if err != nil { + return "", err + } + + return filepath.Clean(dir), nil +} diff --git a/gnovm/pkg/gnofiles/match.go b/gnovm/pkg/gnofiles/match.go new file mode 100644 index 00000000000..f95b0274011 --- /dev/null +++ b/gnovm/pkg/gnofiles/match.go @@ -0,0 +1,384 @@ +package gnofiles + +import ( + "errors" + "fmt" + "io/fs" + "os" + "path" + "path/filepath" + "regexp" + "strings" +) + +// Filter filters the given files s with patterns, following the rules +// of [MatchPatterns]. It does not add the implicit gno patterns of [IsGnoFile]. +// +// Filter panics if any of the patterns is invalid. +func Filter(s []string, patterns ...string) []string { + ret := make([]string, 0, len(s)) + for _, v := range s { + ok, err := MatchPatterns(v, patterns...) + if err != nil { + panic(err) + } + if ok { + ret = append(ret, v) + } + } + return ret +} + +// MatchPatterns returns whether the string s matches all of the given glob-like +// patterns. +// +// - Without any modifiers, s matches the given pattern according to the rules +// of [path.Match]. +// - If a pattern begins with !, it is negated. +// - If a pattern is surrounded by forward slashes (/), it is interpreted as a +// regular expression. +// - A pattern may combine negation and regex; ie. "!/hello.*\.go/" +// - Regular expressions receive the whole path; glob patterns only receive the +// last element (path.Base). +// +// An error is returned only if the patterns have an invalid syntax. +func MatchPatterns(s string, patterns ...string) (bool, error) { + // TODO: does a regex cache make sense here? + bs := []byte(s) + for _, pattern := range patterns { + var negate bool + if strings.HasPrefix(pattern, "!") { + negate = true + pattern = pattern[1:] + } + var res bool + var err error + if len(pattern) > 1 && pattern[0] == '/' && pattern[len(pattern)-1] == '/' { + pattern = pattern[1 : len(pattern)-1] + //nolint:forbidigo + res, err = regexp.Match(pattern, bs) + } else { + res, err = path.Match(pattern, path.Base(s)) + } + if err != nil { + return false, fmt.Errorf("pattern %q: %w", pattern, err) + } + if res == negate { + return false, nil + } + } + return true, nil +} + +type matchOptions struct { + files bool + patterns []string + noImplicit bool + disableEllipsis bool + fs fs.FS +} + +// MatchOption is an option to be passed to [Match] to modify its behavior. +type MatchOption func(c *matchOptions) + +// MatchFiles instructs [Match] to find files instead of packages. +// The file names must match the given patterns, with the same rules/format as +// [MatchPatterns]. Implicitly, all files must not start with "." and must +// end with ".gno". +func MatchFiles(patterns ...string) MatchOption { + return func(m *matchOptions) { m.files, m.patterns = true, patterns } +} + +// MatchPackages instructs [Match] to find packages instead of files. This +// is the default behaviour. A package is defined as a directory containing at +// least one file ending with ".gno" and not starting with ".gno". +// Additional requirement patterns may be specified -- these apply to filenames, +// not directory names. +func MatchPackages(patterns ...string) MatchOption { + return func(m *matchOptions) { m.files, m.patterns = false, patterns } +} + +// MatchNoImplicit removes the implicit gno filters of [Match]. +// This allows Match to be used, ie., for go files. +func MatchNoImplicit() MatchOption { + return func(m *matchOptions) { m.noImplicit = true } +} + +// MatchEllipsis sets whether to use the ellipsis syntax, as in Go, to match +// packages and files. +// +// When this is enabled, the string "/..." is treated as a wildcard and matches +// any string. +// +// The default behaviour is MatchEllipsis(true). +func MatchEllipsis(b bool) MatchOption { + return func(m *matchOptions) { m.disableEllipsis = !b } +} + +// matches ellipsis as separators. +var ( + reEllipsis = regexp.MustCompile(`(?:^|/)\.\.\.(?:/|$)`) + reEllipsisEsc = regexp.MustCompile(`(?:^|/)\\\.\\\.\\\.(/|$)`) +) + +// Match is a central function to parse a set of arguments that expand to a set of +// Gno packages or files. [MatchOptions] may be provided to customise the +// matching behaviour of Match. +// +// By default, Match returns a list of packages matching the patterns in args, +// as well as any "explicit" file passed to it. +func Match(paths []string, opts ...MatchOption) ([]string, error) { + // Determine options. + var c matchOptions + for _, opt := range opts { + opt(&c) + } + + // Set defaults for c.fs, append standard patterns. + var root string + if c.fs == nil { + c.fs = os.DirFS("/") + wd, err := os.Getwd() + if err != nil { + return nil, err + } + root = strings.ReplaceAll(wd, string(os.PathSeparator), "/") + // remove leading / + root = root[1:] + } + if !c.noImplicit { + c.patterns = append(c.patterns, "*.gno", "!.*") + } + + // found will contain the set of matched packages or files. + var found []string + + for _, arg := range paths { + // TODO: eventually we might want to support go-style arguments, + // where we can pass in a package/realm path, ie: + // go test gno.land/p/demo/avl + // for now only work on local FS + + // Normalize separator to /, clean the path, and join it with the root + // if it's relative. + clean := path.Clean(strings.ReplaceAll(arg, string(os.PathSeparator), "/")) + originalClean := clean + if path.IsAbs(clean) { + clean = clean[1:] + if clean == "" { + clean = "." + } + } else { + clean = path.Join(root, clean) + } + if !fs.ValidPath(clean) { + return nil, fmt.Errorf("invalid path: %q", arg) + } + + // Find any valid ellipsis syntax. + ellipsis := reEllipsis.FindStringIndex(clean) + if c.disableEllipsis || ellipsis == nil { + // No ellipsis, or they are disabled -- stat the path directly. + f, err := fs.Stat(c.fs, clean) + if err != nil { + // stat error will already contain path + return nil, err + } + // Explicit file. + if !f.IsDir() { + found = append(found, revertPath(root, originalClean, clean)) + continue + } + // Directory, collect all files matching our patterns. + files, err := collectMatchingFiles(c, clean) + if err != nil { + return nil, err + } + switch { + case len(files) == 0: + if c.noImplicit { + return nil, fmt.Errorf("dir %s: no matching files", arg) + } + return nil, fmt.Errorf("dir %s: no valid gno files", arg) + case c.files: + for _, file := range files { + found = append(found, revertPath(root, originalClean, file)) + } + default: + if clean == "." { + clean = "" + } + found = append(found, revertPath(root, originalClean, clean)) + } + continue + } + + // Find directory to walk. + baseDir := clean[:ellipsis[0]] + if baseDir == "" { + baseDir = "." + } + + // Use regexp for linear-time matching + // Change wildcards to be regex. They will match all directories except for hidden dirs. + // Note that the regex matches only directory names, not filenames. + reString := reEllipsisEsc.ReplaceAllString( + regexp.QuoteMeta(clean), "(?:(?:/|^)[^./][^/]*)*$1", + ) + // for single triple dot, also allow a single "." as a valid package path. + if clean == "..." { + reString = `(?:\.|` + reString + `)` + } + reString = "^" + reString + "$" + re := regexp.MustCompile(reString) + pathTrim := strings.TrimSuffix(baseDir, "/") + fi, err := fs.Stat(c.fs, path.Clean(pathTrim)) + if err != nil { + return nil, err + } + err = walkDir(c.fs, pathTrim, &statDirEntry{fi}, func(fsPath string, entry fs.DirEntry, _ error) error { + // BFS guarantees that we get a dir, its files, then its subdirs. + if entry.IsDir() { + if !re.MatchString(fsPath) { + return fs.SkipDir + } + return nil + } + ok, err := MatchPatterns(fsPath, c.patterns...) + if err != nil { + return err + } + if !ok { + return nil + } + if c.files { + found = append(found, revertPath(root, originalClean, fsPath)) + return nil + } + dirPath := path.Dir(fsPath) + if dirPath == "." { + dirPath = "" + } + found = append(found, revertPath(root, originalClean, dirPath)) + // we found a gno file in this dir; so let's go look at subdirs. + return fs.SkipDir + }) + if err != nil { + return nil, err + } + } + + return found, nil +} + +// Reverts a "clean" path to be closer to its original form, helper function for Match. +// Original absolute -> add a slash to clean path. +// Original relative -> determine the number of prefixed `../` in the clean path, +// store root + n*`../` as prefix, save back as n*`../` + trimprefix(fullpath, prefix) +// cwd: user CWD for relative paths: home/user +// original: original path specified by user, cleaned: ../user2/file.gno +// clean: clean matched path, absolute but without leading slash: home/user2/file.gno +func revertPath(cwd, original, clean string) string { + if path.IsAbs(original) { + return filepath.Join(filepath.VolumeName(original), filepath.FromSlash("/"+clean)) + } + var dotdot int + for ; strings.HasPrefix(original[dotdot*3:], "../"); dotdot++ { //nolint:revive + } + + dots := original[:dotdot*3] // ../ + pref := path.Join(cwd, dots) // /home + if pref == clean { + return "." + } + res := dots + strings.TrimPrefix(clean, pref+"/") + return res +} + +type statDirEntry struct { + info fs.FileInfo +} + +func (d *statDirEntry) Name() string { return d.info.Name() } +func (d *statDirEntry) IsDir() bool { return d.info.IsDir() } +func (d *statDirEntry) Type() fs.FileMode { return d.info.Mode().Type() } +func (d *statDirEntry) Info() (fs.FileInfo, error) { return d.info, nil } + +// walkDir is mostly copied from fs.WalkDir, with a few modifications: +// +// - search is performed breadth-first instead of depth-first. +// - fs.SkipDir is not recursive, and only skips processing the current directory. +// - the argument to this (recursive) function must be a directory. +func walkDir(fsys fs.FS, name string, d fs.DirEntry, walkDirFn fs.WalkDirFunc) error { + var skipDir bool + if err := walkDirFn(name, d, nil); err != nil { + if !errors.Is(err, fs.SkipDir) { + return err + } + skipDir = true + } + + files, err := fs.ReadDir(fsys, name) + if err != nil { + // Second call, to report ReadDir error. + err = walkDirFn(name, d, err) + if err != nil { + if errors.Is(err, fs.SkipDir) { + err = nil + } + return err + } + } + + if !skipDir { + for _, file := range files { + if file.IsDir() { + continue + } + name1 := path.Join(name, file.Name()) + if err := walkDirFn(name1, file, nil); err != nil { + if errors.Is(err, fs.SkipDir) { + break + } + return err + } + } + } + + for _, dir := range files { + if !dir.IsDir() { + continue + } + name1 := path.Join(name, dir.Name()) + if err := walkDir(fsys, name1, dir, walkDirFn); err != nil { + return err + } + } + return nil +} + +func collectMatchingFiles(c matchOptions, dir string) (files []string, err error) { + des, err := fs.ReadDir(c.fs, dir) + if err != nil { + return nil, err + } + + for _, de := range des { + if de.IsDir() { + continue + } + fullPath := path.Join(dir, de.Name()) + ok, err := MatchPatterns(fullPath, c.patterns...) + if err != nil { + return nil, err + } + if ok { + files = append(files, fullPath) + // break if we're only looking for packages on the first matching file we find. + if !c.files { + break + } + } + } + return +} diff --git a/gnovm/pkg/gnofiles/match_test.go b/gnovm/pkg/gnofiles/match_test.go new file mode 100644 index 00000000000..a1adccd1d9e --- /dev/null +++ b/gnovm/pkg/gnofiles/match_test.go @@ -0,0 +1,376 @@ +package gnofiles + +import ( + "fmt" + "io/fs" + "strings" + "testing" + "testing/fstest" + + "github.com/stretchr/testify/assert" +) + +func TestMatchPatterns(t *testing.T) { + tt := []struct { + name string + patterns []string + // if both nil, expected to error + success []string + fail []string + }{ + { + "basic", + []string{"*.gno", "!*_test.gno"}, + []string{"hello.gno", "path/to/welcome.gno", "hello/.gno", ".gno"}, + []string{"hello.go", "path/to/welcome.go", "x.gnox", ".gnox"}, + }, + { + "globsReceiveLast", + []string{"path/*.gno"}, + []string{}, + []string{"path/hello.gno", "path/hello.go", "path/hello.gno/xx.gno", "path.gno/hello.gno"}, + }, + { + "globInvalid", + []string{"[unterminated"}, + nil, nil, + }, + { + "negate", + []string{"!*.gno", "!*.go"}, + []string{"negate.sol", "noext", "///", "", ".goa", "a.goa"}, + []string{"hello.go", "path/to/welcome.go", "x.gno", ".gno"}, + }, + { + "basicRegex", + []string{"/hello/"}, + []string{"hello.gno", "x/to/hello/dir", "hello.go"}, + []string{"Hello", "he/llo", "olleh", ".hel.lo"}, + }, + { + "basicRegexNegate", + []string{"!/hello/"}, + []string{"Hello", "he/llo", "olleh", ".hel.lo"}, + []string{"hello.gno", "x/to/hello/dir", "hello.go"}, + }, + { + "regexInvalid", + []string{"/[unmatched/"}, + nil, nil, + }, + } + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + if tc.success == nil && tc.fail == nil { + _, err := MatchPatterns("xx", tc.patterns...) + if err == nil { + t.Errorf("%v expected to error but didn't", tc.patterns) + } + return + } + + for _, s := range tc.success { + ok, err := MatchPatterns(s, tc.patterns...) + if err != nil { + t.Fatal(err) + } + if !ok { + t.Errorf("%q: does not match %v", s, tc.patterns) + } + } + for _, s := range tc.fail { + ok, err := MatchPatterns(s, tc.patterns...) + if err != nil { + t.Fatal(err) + } + if ok { + t.Errorf("%q: matches %v", s, tc.patterns) + } + } + }) + } +} + +func TestIsGnoFile(t *testing.T) { + for _, s := range [...]string{ + "hello.gno", + "file/to/hello.gno", + "hello_test.gno", + "hello_filetest.gno", + } { + if !IsGnoFile(s) { + t.Errorf("%q marked (incorrectly) as not a gno file", s) + } + } + + for _, s := range [...]string{ + "hidden/.hidden.gno", + "onlysuffix/.gno", + "notgno/hello.go", + } { + if IsGnoFile(s) { + t.Errorf("%q marked (incorrectly) as a gno file", s) + } + } +} + +func TestFilter(t *testing.T) { + testSlice := []string{ + "README", + "LICENSE", + "gno.mod", + "file.gno", + "hello.gno", + "x_filetest.gno", + "x_test.gno", + } + assert.Equal(t, Filter(testSlice, "!*.gno"), []string{"README", "LICENSE", "gno.mod"}) + assert.Equal(t, Filter(testSlice, "gno.*"), []string{"gno.mod"}) + assert.Equal(t, Filter(testSlice, "*.gno"), []string{"file.gno", "hello.gno", "x_filetest.gno", "x_test.gno"}) + assert.Equal(t, Filter(testSlice, "*_test.gno"), []string{"x_test.gno"}) + assert.Equal(t, Filter(testSlice, "*_filetest.gno"), []string{"x_filetest.gno"}) + assert.Equal(t, Filter(testSlice, "/_(file)?test.gno$/"), []string{"x_filetest.gno", "x_test.gno"}) +} + +const matchStructure1 = `README +LICENSE +doc.gno +gno.mod +p1/hello.gno +p1/hello_test.gno +p1/hello_filetest.gno +p1/README +p2/goodbye.gno +p2/goodbye_test.gno +p2/goodbye.go +pno/invalid.go +.hidden/ +.hidden/test.gno` + +const matchStructure2 = `d1/d2/hello/d3/pkg.gno +d1/d2/world/d3/pkg.gno +d1/d2/d3/pkg.gno` + +const matchStructure3 = `foo/bar/x.gno +foo/bar/baz/x.gno +foo/baz/bar/x.gno +foo/x.gno +bar/x.gno` + +func generateFS(structure string) fs.FS { + parts := strings.Split(structure, "\n") + mfs := make(fstest.MapFS, len(parts)) + for _, part := range parts { + part = strings.TrimSpace(part) + if part == "" { + continue + } + mfs[part] = &fstest.MapFile{Data: []byte("Xxx")} + } + return mfs +} + +func Test_generateStructure(t *testing.T) { + t.Skip("Skipping (activate for debugging)") + buf := new(strings.Builder) + fs.WalkDir(generateFS(matchStructure1), ".", func(path string, d fs.DirEntry, err error) error { + fi, err := d.Info() + if err != nil { + return err + } + fmt.Fprintf(buf, "%s: %v\n", path, fi.Mode()) + return nil + }) + t.Log(buf.String()) +} + +func TestMatch(t *testing.T) { + tt := []struct { + name string + structure string + paths []string + opts []MatchOption + exp []string + errContains string + }{ + { + "basic", matchStructure1, + + []string{"./p1"}, + nil, + + []string{"p1"}, + "", + }, + { + "basicFiles", matchStructure1, + + []string{"./p1"}, + []MatchOption{MatchFiles("!*_filetest.gno")}, + + []string{"p1/hello.gno", "p1/hello_test.gno"}, + "", + }, + { + "explicit", matchStructure1, + + []string{"./p1", "./p1/hello_test.gno"}, + []MatchOption{MatchFiles("!*_test.gno", "!*_filetest.gno")}, + + []string{"p1/hello.gno", "p1/hello_test.gno"}, + "", + }, + { + "root", matchStructure1, + + []string{"/", "/p1"}, + nil, + + []string{"/", "/p1"}, + "", + }, + { + "ellipsis", matchStructure1, + + []string{"..."}, + nil, + + []string{".", "p1", "p2"}, + "", + }, + { + "onlyTestPackages", matchStructure1, + + []string{"..."}, + []MatchOption{MatchPackages("*_test.gno")}, + + []string{"p1", "p2"}, + "", + }, + { + "hidden", matchStructure1, + + []string{"...", "./.hidden"}, + nil, + + []string{".", "p1", "p2", ".hidden"}, + "", + }, + { + "subEllipsis", matchStructure2, + + []string{"/d1/d2/.../d3"}, + nil, + + []string{"/d1/d2/d3", "/d1/d2/hello/d3", "/d1/d2/world/d3"}, + "", + }, + { + "subEllipsisPackage", matchStructure2, + + []string{"/d1/d2/.../d3"}, + []MatchOption{MatchFiles()}, + + []string{"/d1/d2/d3/pkg.gno", "/d1/d2/hello/d3/pkg.gno", "/d1/d2/world/d3/pkg.gno"}, + "", + }, + { + "matchNone", matchStructure1, + + []string{"pno/..."}, + nil, + + nil, + "", + }, + { + "goMatchTest", matchStructure3, + + []string{"foo/bar/..."}, + nil, + + []string{"foo/bar", "foo/bar/baz"}, + "", + }, + { + "goMatchTest2", matchStructure3, + + []string{"foo/.../baz"}, + nil, + + []string{"foo/bar/baz"}, + "", + }, + + { + "errInvalidPath", matchStructure1, + + []string{"/", "../../../../"}, + nil, + + nil, + `invalid path: "../../`, + }, + { + "errNotFound", matchStructure1, + + []string{"not_exist"}, + nil, + + nil, + `file does not exist`, + }, + { + "errNotPackage", matchStructure1, + + []string{"p1", "pno", "p2"}, + nil, + + nil, + `dir pno: no valid gno files`, + }, + { + "errBadPattern", matchStructure1, + + []string{"p1"}, + []MatchOption{MatchFiles("[unmatched")}, + + nil, + `syntax error in pattern`, + }, + } + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + f := generateFS(tc.structure) + res, err := Match(tc.paths, append(tc.opts, func(c *matchOptions) { + c.fs = f + })...) + if tc.errContains == "" { + assert.Nil(t, err, "%v", err) + assert.Equal(t, res, tc.exp) + } else { + _ = assert.NotNil(t, err) && + assert.Contains(t, err.Error(), tc.errContains) + assert.Equal(t, res, tc.exp) + } + }) + } +} + +func Test_revertPath(t *testing.T) { + tt := []struct { + name string + cwd, original, clean string + result string + }{ + {"basic", "home/morgan", "file.gno", "home/morgan/file.gno", "file.gno"}, + {"sub", "home/morgan", "t1/file.gno", "home/morgan/t1/file.gno", "t1/file.gno"}, + {"dot", "home/moul", ".", "home/moul", "."}, + {"dotdot", "home/morgan", "../file.gno", "home/file.gno", "../file.gno"}, + } + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + assert.Equal(t, revertPath(tc.cwd, tc.original, tc.clean), tc.result, "result should match") + }) + } +} diff --git a/gnovm/pkg/gnolang/debugger_test.go b/gnovm/pkg/gnolang/debugger_test.go index 926ff0595e6..9c1a5c193ad 100644 --- a/gnovm/pkg/gnolang/debugger_test.go +++ b/gnovm/pkg/gnolang/debugger_test.go @@ -6,13 +6,16 @@ import ( "fmt" "io" "net" + "path/filepath" "strings" "testing" "time" "github.com/gnolang/gno/gnovm/pkg/gnoenv" "github.com/gnolang/gno/gnovm/pkg/gnolang" + "github.com/gnolang/gno/gnovm/pkg/packages" "github.com/gnolang/gno/gnovm/pkg/test" + "github.com/stretchr/testify/require" ) type dtest struct{ in, out string } @@ -24,7 +27,7 @@ type writeNopCloser struct{ io.Writer } func (writeNopCloser) Close() error { return nil } // TODO (Marc): move evalTest to gnovm/tests package and remove code duplicates -func evalTest(debugAddr, in, file string) (out, err string) { +func evalTest(debugAddr, in, file string, pkgs packages.PackagesMap) (out, err string) { bout := bytes.NewBufferString("") berr := bytes.NewBufferString("") stdin := bytes.NewBufferString(in) @@ -39,7 +42,7 @@ func evalTest(debugAddr, in, file string) (out, err string) { err = strings.TrimSpace(strings.ReplaceAll(err, "../../tests/files/", "files/")) }() - _, testStore := test.Store(gnoenv.RootDir(), false, stdin, stdout, stderr) + _, testStore := test.Store(gnoenv.RootDir(), pkgs, false, stdin, stdout, stderr) f := gnolang.MustReadFile(file) @@ -68,12 +71,12 @@ func evalTest(debugAddr, in, file string) (out, err string) { return } -func runDebugTest(t *testing.T, targetPath string, tests []dtest) { +func runDebugTest(t *testing.T, targetPath string, tests []dtest, pkgs packages.PackagesMap) { t.Helper() for _, test := range tests { t.Run("", func(t *testing.T) { - out, err := evalTest("", test.in, targetPath) + out, err := evalTest("", test.in, targetPath, pkgs) t.Log("in:", test.in, "out:", out, "err:", err) if !strings.Contains(out, test.out) { t.Errorf("unexpected output\nwant\"%s\"\n got \"%s\"", test.out, out) @@ -87,6 +90,10 @@ func TestDebug(t *testing.T) { cont := brk + "continue\n" cont2 := "break 21\ncontinue\n" + pkgs, err := packages.Load(&packages.LoadConfig{}, filepath.FromSlash("../../../examples/...")) + require.NoError(t, err) + pkgsMap := packages.NewPackagesMap(pkgs...) + runDebugTest(t, debugTarget, []dtest{ {in: "\n", out: "Welcome to the Gnovm debugger. Type 'help' for list of commands."}, {in: "help\n", out: "The following commands are available"}, @@ -143,18 +150,18 @@ func TestDebug(t *testing.T) { {in: "b 27\nc\np b\n", out: `("!zero" string)`}, {in: "b 22\nc\np t.A[3]\n", out: "Command failed: &{(\"slice index out of bounds: 3 (len=3)\" string) }"}, {in: "b 43\nc\nc\nc\np i\ndetach\n", out: "(1 int)"}, - }) + }, pkgsMap) runDebugTest(t, "../../tests/files/a1.gno", []dtest{ {in: "l\n", out: "unknown source file"}, {in: "b 5\n", out: "unknown source file"}, - }) + }, pkgsMap) runDebugTest(t, "../../tests/integ/debugger/sample2.gno", []dtest{ {in: "s\np tests\n", out: "(package(tests gno.land/p/demo/tests) package{})"}, {in: "s\np tests.World\n", out: `("world" string)`}, {in: "s\np tests.xxx\n", out: "Command failed: invalid selector: xxx"}, - }) + }, pkgsMap) } const debugAddress = "localhost:17358" @@ -166,7 +173,7 @@ func TestRemoteDebug(t *testing.T) { retry int ) - go evalTest(debugAddress, "", debugTarget) + go evalTest(debugAddress, "", debugTarget, nil) for retry = 100; retry > 0; retry-- { conn, err = net.Dial("tcp", debugAddress) @@ -189,7 +196,7 @@ func TestRemoteDebug(t *testing.T) { } func TestRemoteError(t *testing.T) { - _, err := evalTest(":xxx", "", debugTarget) + _, err := evalTest(":xxx", "", debugTarget, nil) t.Log("err:", err) if !strings.Contains(err, "tcp/xxx: unknown port") && !strings.Contains(err, "tcp/xxx: nodename nor servname provided, or not known") { diff --git a/gnovm/pkg/gnolang/files_test.go b/gnovm/pkg/gnolang/files_test.go index 2c82f6d8f29..f6d3a9480b7 100644 --- a/gnovm/pkg/gnolang/files_test.go +++ b/gnovm/pkg/gnolang/files_test.go @@ -12,6 +12,7 @@ import ( "testing" "github.com/gnolang/gno/gnovm/pkg/gnolang" + "github.com/gnolang/gno/gnovm/pkg/packages" "github.com/gnolang/gno/gnovm/pkg/test" "github.com/stretchr/testify/require" ) @@ -38,6 +39,10 @@ func TestFiles(t *testing.T) { rootDir, err := filepath.Abs("../../../") require.NoError(t, err) + pkgs, err := packages.Load(&packages.LoadConfig{}, filepath.Join(rootDir, "examples", "....")) + require.NoError(t, err) + pkgsMap := packages.NewPackagesMap(pkgs...) + newOpts := func() *test.TestOptions { o := &test.TestOptions{ RootDir: rootDir, @@ -46,7 +51,7 @@ func TestFiles(t *testing.T) { Sync: *withSync, } o.BaseStore, o.TestStore = test.Store( - rootDir, true, + rootDir, pkgsMap, true, nopReader{}, o.WriterForStore(), io.Discard, ) return o @@ -121,7 +126,7 @@ func TestStdlibs(t *testing.T) { capture = new(bytes.Buffer) out = capture } - opts = test.NewTestOptions(rootDir, nopReader{}, out, out) + opts = test.NewTestOptions(rootDir, make(map[string]*packages.Package), nopReader{}, out, out) opts.Verbose = true return } diff --git a/gnovm/pkg/gnolang/go2gno.go b/gnovm/pkg/gnolang/go2gno.go index 82d5c69b08b..e7f14393dba 100644 --- a/gnovm/pkg/gnolang/go2gno.go +++ b/gnovm/pkg/gnolang/go2gno.go @@ -500,19 +500,19 @@ type MemPackageGetter interface { // // If format is true, the code will be automatically updated with the // formatted source code. -func TypeCheckMemPackage(mempkg *gnovm.MemPackage, getter MemPackageGetter, format bool) error { - return typeCheckMemPackage(mempkg, getter, false, format) +func TypeCheckMemPackage(pkgDir string, mempkg *gnovm.MemPackage, getter MemPackageGetter, format bool) error { + return typeCheckMemPackage(pkgDir, mempkg, getter, false, format) } // TypeCheckMemPackageTest performs the same type checks as [TypeCheckMemPackage], // but allows re-declarations. // // Note: like TypeCheckMemPackage, this function ignores tests and filetests. -func TypeCheckMemPackageTest(mempkg *gnovm.MemPackage, getter MemPackageGetter) error { - return typeCheckMemPackage(mempkg, getter, true, false) +func TypeCheckMemPackageTest(pkgDir string, mempkg *gnovm.MemPackage, getter MemPackageGetter) error { + return typeCheckMemPackage(pkgDir, mempkg, getter, true, false) } -func typeCheckMemPackage(mempkg *gnovm.MemPackage, getter MemPackageGetter, testing, format bool) error { +func typeCheckMemPackage(pkgDir string, mempkg *gnovm.MemPackage, getter MemPackageGetter, testing, format bool) error { var errs error imp := &gnoImporter{ getter: getter, @@ -526,7 +526,11 @@ func typeCheckMemPackage(mempkg *gnovm.MemPackage, getter MemPackageGetter, test } imp.cfg.Importer = imp - _, err := imp.parseCheckMemPackage(mempkg, format) + if pkgDir == "" { + pkgDir = mempkg.Path + } + + _, err := imp.parseCheckMemPackage(pkgDir, mempkg, format) // prefer to return errs instead of err: // err will generally contain only the first error encountered. if errs != nil { @@ -571,12 +575,12 @@ func (g *gnoImporter) ImportFrom(path, _ string, _ types.ImportMode) (*types.Pac return nil, err } fmt := false - result, err := g.parseCheckMemPackage(mpkg, fmt) + result, err := g.parseCheckMemPackage("", mpkg, fmt) g.cache[path] = gnoImporterResult{pkg: result, err: err} return result, err } -func (g *gnoImporter) parseCheckMemPackage(mpkg *gnovm.MemPackage, fmt bool) (*types.Package, error) { +func (g *gnoImporter) parseCheckMemPackage(pkgDir string, mpkg *gnovm.MemPackage, fmt bool) (*types.Package, error) { // This map is used to allow for function re-definitions, which are allowed // in Gno (testing context) but not in Go. // This map links each function identifier with a closure to remove its @@ -600,7 +604,7 @@ func (g *gnoImporter) parseCheckMemPackage(mpkg *gnovm.MemPackage, fmt bool) (*t } const parseOpts = parser.ParseComments | parser.DeclarationErrors | parser.SkipObjectResolution - f, err := parser.ParseFile(fset, path.Join(mpkg.Path, file.Name), file.Body, parseOpts) + f, err := parser.ParseFile(fset, path.Join(pkgDir, file.Name), file.Body, parseOpts) if err != nil { errs = multierr.Append(errs, err) continue diff --git a/gnovm/pkg/gnolang/go2gno_test.go b/gnovm/pkg/gnolang/go2gno_test.go index 8aba5d7f293..83365cfb0a6 100644 --- a/gnovm/pkg/gnolang/go2gno_test.go +++ b/gnovm/pkg/gnolang/go2gno_test.go @@ -325,7 +325,7 @@ func TestTypeCheckMemPackage(t *testing.T) { t.Parallel() format := false - err := TypeCheckMemPackage(tc.pkg, tc.getter, format) + err := TypeCheckMemPackage("", tc.pkg, tc.getter, format) if tc.check == nil { assert.NoError(t, err) } else { @@ -360,7 +360,7 @@ func TestTypeCheckMemPackage_format(t *testing.T) { mpkgGetter := mockPackageGetter{} format := false - err := TypeCheckMemPackage(pkg, mpkgGetter, format) + err := TypeCheckMemPackage("", pkg, mpkgGetter, format) assert.NoError(t, err) assert.Equal(t, input, pkg.Files[0].Body) // unchanged @@ -372,7 +372,7 @@ func Hello(name string) string { ` format = true - err = TypeCheckMemPackage(pkg, mpkgGetter, format) + err = TypeCheckMemPackage("", pkg, mpkgGetter, format) assert.NoError(t, err) assert.NotEqual(t, input, pkg.Files[0].Body) assert.Equal(t, expected, pkg.Files[0].Body) diff --git a/gnovm/pkg/gnolang/store.go b/gnovm/pkg/gnolang/store.go index bc56a7c6313..0a5355570b7 100644 --- a/gnovm/pkg/gnolang/store.go +++ b/gnovm/pkg/gnolang/store.go @@ -3,6 +3,7 @@ package gnolang import ( "fmt" "reflect" + dbg "runtime/debug" "slices" "strconv" "strings" @@ -711,6 +712,7 @@ func (ds *defaultStore) GetBlockNodeSafe(loc Location) BlockNode { func (ds *defaultStore) SetBlockNode(bn BlockNode) { loc := bn.GetLocation() if loc.IsZero() { + dbg.PrintStack() panic("unexpected zero location in blocknode") } // save node to backend. diff --git a/gnovm/pkg/gnolang/testdata/corpra/parsefile/bug_3013.go b/gnovm/pkg/gnolang/testdata/corpra/parsefile/bug_3013.go index f664f68f1b6..1ddb7f51aa5 100644 --- a/gnovm/pkg/gnolang/testdata/corpra/parsefile/bug_3013.go +++ b/gnovm/pkg/gnolang/testdata/corpra/parsefile/bug_3013.go @@ -3,20 +3,20 @@ package main import "testing" func TestDummy(t *testing.T) { - testTable := []struct { - name string - }{ - { - "one", - }, - { - "two", - }, - } + testTable := []struct { + name string + }{ + { + "one", + }, + { + "two", + }, + } - for _, testCase := range testTable { - testCase := testCase + for _, testCase := range testTable { + testCase := testCase - println(testCase.name) - } + println(testCase.name) + } } diff --git a/gnovm/pkg/gnomod/gnomod.go b/gnovm/pkg/gnomod/gnomod.go index a34caa2e48d..d41cb47ae0b 100644 --- a/gnovm/pkg/gnomod/gnomod.go +++ b/gnovm/pkg/gnomod/gnomod.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/gnolang/gno/gnovm/pkg/gnoenv" + "github.com/gnolang/gno/gnovm/pkg/gnofiles" "github.com/gnolang/gno/gnovm/pkg/gnolang" "golang.org/x/mod/modfile" "golang.org/x/mod/module" @@ -47,7 +48,7 @@ func CreateGnoModFile(rootDir, modPath string) error { var pkgName gnolang.Name for _, file := range files { - if file.IsDir() || !strings.HasSuffix(file.Name(), ".gno") || strings.HasSuffix(file.Name(), "_filetest.gno") { + if file.IsDir() || !gnofiles.IsGnoFile(file.Name(), "!*_filetest.gno") { continue } diff --git a/gnovm/pkg/gnomod/parse_test.go b/gnovm/pkg/gnomod/parse_test.go index ec54c6424fc..a2410cec8d8 100644 --- a/gnovm/pkg/gnomod/parse_test.go +++ b/gnovm/pkg/gnomod/parse_test.go @@ -1,6 +1,7 @@ package gnomod import ( + "os" "path/filepath" "testing" @@ -237,3 +238,16 @@ func TestParseGnoMod(t *testing.T) { }) } } + +func createGnoModPkg(t *testing.T, dirPath, pkgName, modData string) { + t.Helper() + + // Create package dir + pkgDirPath := filepath.Join(dirPath, pkgName) + err := os.MkdirAll(pkgDirPath, 0o755) + require.NoError(t, err) + + // Create gno.mod + err = os.WriteFile(filepath.Join(pkgDirPath, "gno.mod"), []byte(modData), 0o644) + require.NoError(t, err) +} diff --git a/gnovm/pkg/gnomod/pkg.go b/gnovm/pkg/gnomod/pkg.go deleted file mode 100644 index 85f1d31442d..00000000000 --- a/gnovm/pkg/gnomod/pkg.go +++ /dev/null @@ -1,270 +0,0 @@ -package gnomod - -import ( - "fmt" - "io/fs" - "os" - "path/filepath" - "strings" - - "github.com/gnolang/gno/gnovm" - "github.com/gnolang/gno/gnovm/pkg/gnolang" - "github.com/gnolang/gno/gnovm/pkg/packages" -) - -type Pkg struct { - Dir string // absolute path to package dir - Name string // package name - Imports []string // direct imports of this pkg - Draft bool // whether the package is a draft -} - -type SubPkg struct { - Dir string // absolute path to package dir - ImportPath string // import path of package - Root string // Root dir containing this package, i.e dir containing gno.mod file - Imports []string // imports used by this package - - GnoFiles []string // .gno source files (excluding TestGnoFiles, FiletestGnoFiles) - TestGnoFiles []string // _test.gno source files - FiletestGnoFiles []string // _filetest.gno source files -} - -type ( - PkgList []Pkg - SortedPkgList []Pkg -) - -// sortPkgs sorts the given packages by their dependencies. -func (pl PkgList) Sort() (SortedPkgList, error) { - visited := make(map[string]bool) - onStack := make(map[string]bool) - sortedPkgs := make([]Pkg, 0, len(pl)) - - // Visit all packages - for _, p := range pl { - if err := visitPackage(p, pl, visited, onStack, &sortedPkgs); err != nil { - return nil, err - } - } - - return sortedPkgs, nil -} - -// visitNode visits a package's and its dependencies dependencies and adds them to the sorted list. -func visitPackage(pkg Pkg, pkgs []Pkg, visited, onStack map[string]bool, sortedPkgs *[]Pkg) error { - if onStack[pkg.Name] { - return fmt.Errorf("cycle detected: %s", pkg.Name) - } - if visited[pkg.Name] { - return nil - } - - visited[pkg.Name] = true - onStack[pkg.Name] = true - - // Visit package's dependencies - for _, imp := range pkg.Imports { - found := false - for _, p := range pkgs { - if p.Name != imp { - continue - } - if err := visitPackage(p, pkgs, visited, onStack, sortedPkgs); err != nil { - return err - } - found = true - break - } - if !found { - return fmt.Errorf("missing dependency '%s' for package '%s'", imp, pkg.Name) - } - } - - onStack[pkg.Name] = false - *sortedPkgs = append(*sortedPkgs, pkg) - return nil -} - -// ListPkgs lists all gno packages in the given root directory. -func ListPkgs(root string) (PkgList, error) { - var pkgs []Pkg - - err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error { - if err != nil { - return err - } - if !d.IsDir() { - return nil - } - gnoModPath := filepath.Join(path, "gno.mod") - data, err := os.ReadFile(gnoModPath) - if os.IsNotExist(err) { - return nil - } - if err != nil { - return err - } - - gnoMod, err := Parse(gnoModPath, data) - if err != nil { - return fmt.Errorf("parse: %w", err) - } - gnoMod.Sanitize() - if err := gnoMod.Validate(); err != nil { - return fmt.Errorf("validate: %w", err) - } - - pkg, err := gnolang.ReadMemPackage(path, gnoMod.Module.Mod.Path) - if err != nil { - // ignore package files on error - pkg = &gnovm.MemPackage{} - } - - importsMap, err := packages.Imports(pkg, nil) - if err != nil { - // ignore imports on error - importsMap = nil - } - importsRaw := importsMap.Merge(packages.FileKindPackageSource, packages.FileKindTest, packages.FileKindXTest) - - imports := make([]string, 0, len(importsRaw)) - for _, imp := range importsRaw { - // remove self and standard libraries from imports - if imp.PkgPath != gnoMod.Module.Mod.Path && - !gnolang.IsStdlib(imp.PkgPath) { - imports = append(imports, imp.PkgPath) - } - } - - pkgs = append(pkgs, Pkg{ - Dir: path, - Name: gnoMod.Module.Mod.Path, - Draft: gnoMod.Draft, - Imports: imports, - }) - return nil - }) - if err != nil { - return nil, err - } - - return pkgs, nil -} - -// GetNonDraftPkgs returns packages that are not draft -// and have no direct or indirect draft dependencies. -func (sp SortedPkgList) GetNonDraftPkgs() SortedPkgList { - res := make([]Pkg, 0, len(sp)) - draft := make(map[string]bool) - - for _, pkg := range sp { - if pkg.Draft { - draft[pkg.Name] = true - continue - } - dependsOnDraft := false - for _, req := range pkg.Imports { - if draft[req] { - dependsOnDraft = true - draft[pkg.Name] = true - break - } - } - if !dependsOnDraft { - res = append(res, pkg) - } - } - return res -} - -// SubPkgsFromPaths returns a list of subpackages from the given paths. -func SubPkgsFromPaths(paths []string) ([]*SubPkg, error) { - for _, path := range paths { - fi, err := os.Stat(path) - if err != nil { - return nil, err - } - if fi.IsDir() { - continue - } - if filepath.Ext(path) != ".gno" { - return nil, fmt.Errorf("files must be .gno files: %s", path) - } - - subPkg, err := GnoFileSubPkg(paths) - if err != nil { - return nil, err - } - return []*SubPkg{subPkg}, nil - } - - subPkgs := make([]*SubPkg, 0, len(paths)) - for _, path := range paths { - subPkg := SubPkg{} - - matches, err := filepath.Glob(filepath.Join(path, "*.gno")) - if err != nil { - return nil, fmt.Errorf("failed to match pattern: %w", err) - } - - subPkg.Dir = path - for _, match := range matches { - if strings.HasSuffix(match, "_test.gno") { - subPkg.TestGnoFiles = append(subPkg.TestGnoFiles, match) - continue - } - - if strings.HasSuffix(match, "_filetest.gno") { - subPkg.FiletestGnoFiles = append(subPkg.FiletestGnoFiles, match) - continue - } - subPkg.GnoFiles = append(subPkg.GnoFiles, match) - } - - subPkgs = append(subPkgs, &subPkg) - } - - return subPkgs, nil -} - -// GnoFileSubPkg returns a subpackage from the given .gno files. -func GnoFileSubPkg(files []string) (*SubPkg, error) { - subPkg := SubPkg{} - firstDir := "" - for _, file := range files { - if filepath.Ext(file) != ".gno" { - return nil, fmt.Errorf("files must be .gno files: %s", file) - } - - fi, err := os.Stat(file) - if err != nil { - return nil, err - } - if fi.IsDir() { - return nil, fmt.Errorf("%s is a directory, should be a Gno file", file) - } - - dir := filepath.Dir(file) - if firstDir == "" { - firstDir = dir - } - if dir != firstDir { - return nil, fmt.Errorf("all files must be in one directory; have %s and %s", firstDir, dir) - } - - if strings.HasSuffix(file, "_test.gno") { - subPkg.TestGnoFiles = append(subPkg.TestGnoFiles, file) - continue - } - - if strings.HasSuffix(file, "_filetest.gno") { - subPkg.FiletestGnoFiles = append(subPkg.FiletestGnoFiles, file) - continue - } - subPkg.GnoFiles = append(subPkg.GnoFiles, file) - } - subPkg.Dir = firstDir - - return &subPkg, nil -} diff --git a/gnovm/pkg/packages/analyze_packages.go b/gnovm/pkg/packages/analyze_packages.go new file mode 100644 index 00000000000..55fbc27f3b3 --- /dev/null +++ b/gnovm/pkg/packages/analyze_packages.go @@ -0,0 +1,193 @@ +package packages + +import ( + "errors" + "fmt" + "go/token" + "os" + "path" + "path/filepath" + "strings" + + "github.com/gnolang/gno/gnovm" + "github.com/gnolang/gno/gnovm/pkg/gnoenv" + "github.com/gnolang/gno/gnovm/pkg/gnolang" + "github.com/gnolang/gno/gnovm/pkg/gnomod" +) + +func readPackages(matches []*pkgMatch, fset *token.FileSet) ([]*Package, error) { + if fset == nil { + fset = token.NewFileSet() + } + pkgs := make([]*Package, 0, len(matches)) + for _, pkgMatch := range matches { + var pkg *Package + var err error + if pkgMatch.Dir == "command-line-arguments" { + pkg, err = readCLAPkg(pkgMatch.Match, fset) + if err != nil { + return nil, err + } + } else { + pkg = readPkgDir(pkgMatch.Dir, "", fset) + } + pkg.Match = pkgMatch.Match + pkgs = append(pkgs, pkg) + } + return pkgs, nil +} + +func readCLAPkg(patterns []string, fset *token.FileSet) (*Package, error) { + pkg := &Package{ + ImportPath: "command-line-arguments", + Files: make(FilesMap), + Imports: make(ImportsMap), + } + var err error + + files := []string{} + for _, match := range patterns { + dir, base := filepath.Split(match) + dir, err = filepath.Abs(dir) + if err != nil { + return nil, err + } + if pkg.Dir == "" { + pkg.Dir = dir + } else if dir != pkg.Dir { + return nil, fmt.Errorf("named files must all be in one directory; have %s and %s", pkg.Dir, dir) + } + + files = append(files, base) + } + + return readPkgFiles(pkg, files, fset), nil +} + +func readPkgDir(pkgDir string, importPath string, fset *token.FileSet) *Package { + pkg := &Package{ + Dir: pkgDir, + Files: make(FilesMap), + Imports: make(ImportsMap), + ImportPath: importPath, + } + + if pkg.ImportPath == "" { + stdlibsPath := filepath.Join(gnoenv.RootDir(), "gnovm", "stdlibs") + if strings.HasPrefix(filepath.Clean(pkg.Dir), stdlibsPath) { + libPath, err := filepath.Rel(stdlibsPath, pkg.Dir) + if err != nil { + pkg.Errors = append(pkg.Errors, err) + return pkg + } + pkg.ImportPath = libPath + } + } + + files := []string{} + entries, err := os.ReadDir(pkgDir) + if err != nil { + pkg.Errors = append(pkg.Errors, err) + return pkg + } + for _, entry := range entries { + if entry.IsDir() { + continue + } + + base := entry.Name() + + if !strings.HasSuffix(base, ".gno") { + continue + } + + files = append(files, base) + } + + return readPkgFiles(pkg, files, fset) +} + +func readPkgFiles(pkg *Package, files []string, fset *token.FileSet) *Package { + if fset == nil { + fset = token.NewFileSet() + } + + mempkg := gnovm.MemPackage{} + + for _, base := range files { + fpath := filepath.Join(pkg.Dir, base) + + bodyBytes, err := os.ReadFile(fpath) + if err != nil { + pkg.Errors = append(pkg.Errors, err) + continue + } + body := string(bodyBytes) + + fileKind, err := GetFileKind(base, body, fset) + if err != nil { + pkg.Errors = append(pkg.Errors, err) + continue + } + + mempkg.Files = append(mempkg.Files, &gnovm.MemFile{Name: base, Body: body}) + pkg.Files[fileKind] = append(pkg.Files[fileKind], base) + } + + var err error + pkg.Imports, err = Imports(&mempkg, fset) + if err != nil { + pkg.Errors = append(pkg.Errors, err) + } + + // we use the ReadMemPkg utils to get the package name because we want name resolution like the vm + nameFiles := pkg.Files.Merge(FileKindPackageSource, FileKindTest, FileKindXTest) + absFiles := make([]string, 0, len(nameFiles)) + for _, f := range nameFiles { + absFiles = append(absFiles, filepath.Join(pkg.Dir, f)) + } + minMempkg, err := gnolang.ReadMemPackageFromList(absFiles, "") + if err != nil { + pkg.Errors = append(pkg.Errors, err) + } else { + pkg.Name = minMempkg.Name + } + + // TODO: check if stdlib + + if pkg.ImportPath == "command-line-arguments" { + return pkg + } + + pkg.Root, err = gnomod.FindRootDir(pkg.Dir) + if errors.Is(err, gnomod.ErrGnoModNotFound) { + return pkg + } + if err != nil { + pkg.Errors = append(pkg.Errors, err) + return pkg + } + + mod, err := gnomod.ParseGnoMod(filepath.Join(pkg.Root, "gno.mod")) + if err != nil { + pkg.Errors = append(pkg.Errors, err) + return pkg + } + + pkg.Draft = mod.Draft + + if mod.Module == nil { + return pkg + } + + pkg.ModPath = mod.Module.Mod.Path + subPath, err := filepath.Rel(pkg.Root, pkg.Dir) + if err != nil { + pkg.Errors = append(pkg.Errors, err) + return pkg + } + + pkg.ImportPath = path.Join(pkg.ModPath, filepath.ToSlash(subPath)) + + return pkg +} diff --git a/gnovm/cmd/gno/download_deps.go b/gnovm/pkg/packages/download_deps.go similarity index 63% rename from gnovm/cmd/gno/download_deps.go rename to gnovm/pkg/packages/download_deps.go index 4e638eb4970..45a704766ac 100644 --- a/gnovm/cmd/gno/download_deps.go +++ b/gnovm/pkg/packages/download_deps.go @@ -1,4 +1,4 @@ -package main +package packages import ( "errors" @@ -7,17 +7,15 @@ import ( "path/filepath" "strings" - "github.com/gnolang/gno/gnovm/cmd/gno/internal/pkgdownload" "github.com/gnolang/gno/gnovm/pkg/gnolang" "github.com/gnolang/gno/gnovm/pkg/gnomod" - "github.com/gnolang/gno/gnovm/pkg/packages" - "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/gnolang/gno/gnovm/pkg/packages/pkgdownload" "golang.org/x/mod/module" ) -// downloadDeps recursively fetches the imports of a local package while following a given gno.mod replace directives -func downloadDeps(io commands.IO, pkgDir string, gnoMod *gnomod.File, fetcher pkgdownload.PackageFetcher) error { - if fetcher == nil { +// DownloadDeps recursively fetches the imports of a local package while following a given gno.mod replace directives +func DownloadDeps(conf *LoadConfig, pkgDir string, gnoMod *gnomod.File) error { + if conf.Fetcher == nil { return errors.New("fetcher is nil") } @@ -25,14 +23,14 @@ func downloadDeps(io commands.IO, pkgDir string, gnoMod *gnomod.File, fetcher pk if err != nil { return fmt.Errorf("read package at %q: %w", pkgDir, err) } - importsMap, err := packages.Imports(pkg, nil) + importsMap, err := Imports(pkg, nil) if err != nil { return fmt.Errorf("read imports at %q: %w", pkgDir, err) } - imports := importsMap.Merge(packages.FileKindPackageSource, packages.FileKindTest, packages.FileKindXTest) + imports := importsMap.Merge(FileKindPackageSource, FileKindTest, FileKindXTest) for _, pkgPath := range imports { - resolved := gnoMod.Resolve(module.Version{Path: pkgPath.PkgPath}) + resolved := gnoMod.Resolve(module.Version{Path: pkgPath}) resolvedPkgPath := resolved.Path if !isRemotePkgPath(resolvedPkgPath) { @@ -41,11 +39,11 @@ func downloadDeps(io commands.IO, pkgDir string, gnoMod *gnomod.File, fetcher pk depDir := gnomod.PackageDir("", module.Version{Path: resolvedPkgPath}) - if err := downloadPackage(io, resolvedPkgPath, depDir, fetcher); err != nil { + if err := downloadPackage(conf, resolvedPkgPath, depDir); err != nil { return fmt.Errorf("download import %q of %q: %w", resolvedPkgPath, pkgDir, err) } - if err := downloadDeps(io, depDir, gnoMod, fetcher); err != nil { + if err := DownloadDeps(conf, depDir, gnoMod); err != nil { return err } } @@ -53,8 +51,8 @@ func downloadDeps(io commands.IO, pkgDir string, gnoMod *gnomod.File, fetcher pk return nil } -// downloadPackage downloads a remote gno package by pkg path and store it at dst -func downloadPackage(io commands.IO, pkgPath string, dst string, fetcher pkgdownload.PackageFetcher) error { +// Download downloads a remote gno package by pkg path and store it at dst +func downloadPackage(conf *LoadConfig, pkgPath string, dst string) error { modFilePath := filepath.Join(dst, "gno.mod") if _, err := os.Stat(modFilePath); err == nil { @@ -64,9 +62,9 @@ func downloadPackage(io commands.IO, pkgPath string, dst string, fetcher pkgdown return fmt.Errorf("stat downloaded module %q at %q: %w", pkgPath, dst, err) } - io.ErrPrintfln("gno: downloading %s", pkgPath) + conf.IO.ErrPrintfln("gno: downloading %s", pkgPath) - if err := pkgdownload.Download(pkgPath, dst, fetcher); err != nil { + if err := pkgdownload.Download(pkgPath, dst, conf.Fetcher); err != nil { return err } diff --git a/gnovm/cmd/gno/download_deps_test.go b/gnovm/pkg/packages/download_deps_test.go similarity index 94% rename from gnovm/cmd/gno/download_deps_test.go rename to gnovm/pkg/packages/download_deps_test.go index 0828e9b2245..b2d75787ee5 100644 --- a/gnovm/cmd/gno/download_deps_test.go +++ b/gnovm/pkg/packages/download_deps_test.go @@ -1,4 +1,4 @@ -package main +package packages import ( "bytes" @@ -7,8 +7,8 @@ import ( "path/filepath" "testing" - "github.com/gnolang/gno/gnovm/cmd/gno/internal/pkgdownload/examplespkgfetcher" "github.com/gnolang/gno/gnovm/pkg/gnomod" + "github.com/gnolang/gno/gnovm/pkg/packages/pkgdownload/examplespkgfetcher" "github.com/gnolang/gno/tm2/pkg/commands" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -115,8 +115,10 @@ func TestDownloadDeps(t *testing.T) { fetcher := examplespkgfetcher.New() + conf := &LoadConfig{IO: io, Fetcher: fetcher} + // gno: downloading dependencies - err = downloadDeps(io, dirPath, &tc.modFile, fetcher) + err = DownloadDeps(conf, dirPath, &tc.modFile) if tc.errorShouldContain != "" { require.ErrorContains(t, err, tc.errorShouldContain) } else { @@ -142,7 +144,7 @@ func TestDownloadDeps(t *testing.T) { mockErr.Reset() // Try fetching again. Should be cached - downloadDeps(io, dirPath, &tc.modFile, fetcher) + DownloadDeps(conf, dirPath, &tc.modFile) for _, c := range tc.ioErrContains { assert.NotContains(t, mockErr.String(), c) } diff --git a/gnovm/pkg/packages/expand_patterns.go b/gnovm/pkg/packages/expand_patterns.go new file mode 100644 index 00000000000..a4525bdd2a0 --- /dev/null +++ b/gnovm/pkg/packages/expand_patterns.go @@ -0,0 +1,218 @@ +package packages + +import ( + "errors" + "fmt" + "io/fs" + "os" + "path" + "path/filepath" + "slices" + "strings" + + "github.com/gnolang/gno/gnovm/pkg/gnomod" + "golang.org/x/mod/module" +) + +/* +REARCH: +- patterns: + - single file -> not supported + - local directory -> add as package + - remote -> download and add dst + - recursive local -> walk directories at the pattern and select the ones that contain gno.mod or .gno files and add them as packages + - recursive remote -> not supported +*/ + +type pkgMatch struct { + Dir string + Match []string +} + +func expandPatterns(conf *LoadConfig, patterns ...string) ([]*pkgMatch, error) { + pkgMatches := []*pkgMatch{} + + addPkgDir := func(dir string, match *string) { + idx := slices.IndexFunc(pkgMatches, func(sum *pkgMatch) bool { return sum.Dir == dir }) + if idx == -1 { + matches := []string{} + if match != nil { + matches = append(matches, *match) + } + pkgMatches = append(pkgMatches, &pkgMatch{Dir: dir, Match: matches}) + return + } + if match == nil { + return + } + if slices.Contains(pkgMatches[idx].Match, *match) { + return + } + pkgMatches[idx].Match = append(pkgMatches[idx].Match, *match) + } + + kinds := make([]patternKind, 0, len(patterns)) + for _, match := range patterns { + patKind, err := getPatternKind(match) + if err != nil { + return nil, fmt.Errorf("%s: %w", match, err) + } + kinds = append(kinds, patKind) + } + + if slices.Contains(kinds, patternKindSingleFile) { + remaining := []string{} + remainingKinds := []patternKind{} + + files := make([]string, 0, len(patterns)) + for i, match := range patterns { + kind := kinds[i] + if kind != patternKindSingleFile { + remaining = append(remaining, match) + remainingKinds = append(remainingKinds, kind) + continue + } + if !strings.HasSuffix(match, ".gno") { + return nil, fmt.Errorf("named files must be .gno files: %s", match) + } + files = append(files, match) + } + + pkgMatches = append(pkgMatches, &pkgMatch{Dir: "command-line-arguments", Match: files}) + + patterns = remaining + kinds = remainingKinds + } + + for i, match := range patterns { + patKind := kinds[i] + + switch patKind { + case patternKindRecursiveRemote: + return nil, fmt.Errorf("%s: recursive remote patterns are not supported", match) + + case patternKindRemote: + if conf.SelfContained { + return nil, fmt.Errorf("%s: remote patterns are not supported in self-contained mode", match) + } + case patternKindSingleFile: + return nil, fmt.Errorf("unexpected single pattern at this point") + } + + pat, err := cleanPattern(match, patKind) + if err != nil { + return nil, fmt.Errorf("%s: %w", match, err) + } + + switch patKind { + case patternKindDirectory: + addPkgDir(pat, &match) + + case patternKindRemote: + dir := gnomod.PackageDir("", module.Version{Path: pat}) + if err := downloadPackage(conf, pat, dir); err != nil { + return nil, err + } + addPkgDir(dir, &match) + + case patternKindRecursiveLocal: + dirs, err := expandRecursive(pat) + if err != nil { + return nil, fmt.Errorf("%s: %w", pat, err) + } + if len(dirs) == 0 { + conf.IO.ErrPrintfln(`gno: warning: %q matched no packages`, match) + } + for _, dir := range dirs { + addPkgDir(dir, &match) + } + } + } + + return pkgMatches, nil +} + +func expandRecursive(pat string) ([]string, error) { + root, _ := filepath.Split(pat) + + info, err := os.Stat(root) + if err != nil { + return nil, err + } + + if !info.IsDir() { + return nil, errors.New("glob root is not a directory") + } + + // we swallow errors after this point as we want the most packages we can get + dirs := []string{} + _ = fs.WalkDir(os.DirFS(root), ".", func(path string, d fs.DirEntry, err error) error { + if err != nil { + return nil + } + if d.IsDir() { + return nil + } + dir, base := filepath.Split(path) + dir = filepath.Join(root, dir) + if slices.Contains(dirs, dir) { + return nil + } + if base == "gno.mod" || strings.HasSuffix(base, ".gno") { + dirs = append(dirs, dir) + } + return nil + }) + + return dirs, nil +} + +type patternKind int + +const ( + patternKindUnknown = iota + patternKindSingleFile + patternKindDirectory + patternKindRecursiveLocal + patternKindRemote + patternKindRecursiveRemote +) + +func getPatternKind(pat string) (patternKind, error) { + isLitteral := PatternIsLiteral(pat) + + if patternIsRemote(pat) { + if isLitteral { + return patternKindRemote, nil + } + if filepath.Base(pat) != "..." { + return patternKindUnknown, fmt.Errorf("%s: partial globs are not supported", pat) + } + return patternKindRecursiveRemote, nil + } + + if !isLitteral { + return patternKindRecursiveLocal, nil + } + + info, err := os.Stat(pat) + if err != nil { + return patternKindUnknown, err + } + if info.IsDir() { + return patternKindDirectory, nil + } + + return patternKindSingleFile, nil +} + +func cleanPattern(pat string, kind patternKind) (string, error) { + switch kind { + case patternKindSingleFile, patternKindDirectory, patternKindRecursiveLocal: + return filepath.Abs(pat) + case patternKindRemote, patternKindRecursiveRemote: + return path.Clean(pat), nil + default: + return "", fmt.Errorf("unknown pattern kind %d", kind) + } +} diff --git a/gnovm/pkg/packages/filekind.go b/gnovm/pkg/packages/filekind.go index ed2ca84b7d0..88a8ca72e96 100644 --- a/gnovm/pkg/packages/filekind.go +++ b/gnovm/pkg/packages/filekind.go @@ -16,16 +16,20 @@ import ( // - [FileKindXTest] -> A *_test.gno file with a package name ending in _test that will be used for blackbox testing // // - [FileKindFiletest] -> A *_filetest.gno file that will be used for snapshot testing -type FileKind uint +type FileKind string const ( - FileKindUnknown FileKind = iota - FileKindPackageSource - FileKindTest - FileKindXTest - FileKindFiletest + FileKindUnknown FileKind = "" + FileKindPackageSource = "PackageSource" + FileKindTest = "Test" + FileKindXTest = "XTest" + FileKindFiletest = "Filetest" ) +func AllFileKinds() []FileKind { + return []FileKind{FileKindPackageSource, FileKindTest, FileKindXTest, FileKindFiletest} +} + // GetFileKind analyzes a file's name and body to get it's [FileKind], fset is optional func GetFileKind(filename string, body string, fset *token.FileSet) (FileKind, error) { if !strings.HasSuffix(filename, ".gno") { @@ -45,7 +49,7 @@ func GetFileKind(filename string, body string, fset *token.FileSet) (FileKind, e } ast, err := parser.ParseFile(fset, filename, body, parser.PackageClauseOnly) if err != nil { - return FileKindUnknown, err + return FileKindTest, nil } packageName := ast.Name.Name diff --git a/gnovm/pkg/packages/filekind_test.go b/gnovm/pkg/packages/filekind_test.go index bd06b49fb45..735ecd64273 100644 --- a/gnovm/pkg/packages/filekind_test.go +++ b/gnovm/pkg/packages/filekind_test.go @@ -25,6 +25,12 @@ func TestGetFileKind(t *testing.T) { body: "package foo", fileKind: FileKindTest, }, + { + name: "test_badpkgclause", + filename: "foo_test.gno", + body: "pakage foo", + fileKind: FileKindTest, + }, { name: "xtest", filename: "foo_test.gno", @@ -36,12 +42,6 @@ func TestGetFileKind(t *testing.T) { filename: "foo_filetest.gno", fileKind: FileKindFiletest, }, - { - name: "err_badpkgclause", - filename: "foo_test.gno", - body: "pakage foo", - errorContains: "foo_test.gno:1:1: expected 'package', found pakage", - }, { name: "err_notgnofile", filename: "foo.gno.bck", diff --git a/gnovm/pkg/packages/imports.go b/gnovm/pkg/packages/imports.go index 3bc60be6664..f75d6a5c978 100644 --- a/gnovm/pkg/packages/imports.go +++ b/gnovm/pkg/packages/imports.go @@ -12,10 +12,18 @@ import ( "github.com/gnolang/gno/gnovm" ) +func Imports(pkg *gnovm.MemPackage, fset *token.FileSet) (ImportsMap, error) { + specs, err := ImportsSpecs(pkg, fset) + if err != nil { + return nil, err + } + return ImportsMapFromSpecs(specs, fset), nil +} + // Imports returns the list of gno imports from a [gnovm.MemPackage]. // fset is optional. -func Imports(pkg *gnovm.MemPackage, fset *token.FileSet) (ImportsMap, error) { - res := make(ImportsMap, 16) +func ImportsSpecs(pkg *gnovm.MemPackage, fset *token.FileSet) (ImportsSpecsMap, error) { + res := make(ImportsSpecsMap, 16) seen := make(map[FileKind]map[string]struct{}, 16) for _, file := range pkg.Files { @@ -27,38 +35,30 @@ func Imports(pkg *gnovm.MemPackage, fset *token.FileSet) (ImportsMap, error) { if err != nil { return nil, err } - imports, err := FileImports(file.Name, file.Body, fset) + imports, err := FileImportsSpecs(file.Name, file.Body, fset) if err != nil { return nil, err } for _, im := range imports { - if _, ok := seen[fileKind][im.PkgPath]; ok { + if _, ok := seen[fileKind][im.Path.Value]; ok { continue } res[fileKind] = append(res[fileKind], im) if _, ok := seen[fileKind]; !ok { seen[fileKind] = make(map[string]struct{}, 16) } - seen[fileKind][im.PkgPath] = struct{}{} + seen[fileKind][im.Path.Value] = struct{}{} } } for _, imports := range res { - sortImports(imports) + sortImportsSpecs(imports) } return res, nil } -// FileImport represents an import -type FileImport struct { - PkgPath string - Spec *ast.ImportSpec -} - -// FileImports returns the list of gno imports in the given file src. -// The given filename is only used when recording position information. -func FileImports(filename string, src string, fset *token.FileSet) ([]FileImport, error) { +func FileImportsSpecs(filename string, src string, fset *token.FileSet) ([]*ast.ImportSpec, error) { if fset == nil { fset = token.NewFileSet() } @@ -66,36 +66,51 @@ func FileImports(filename string, src string, fset *token.FileSet) ([]FileImport if err != nil { return nil, err } - res := make([]FileImport, len(f.Imports)) + res := make([]*ast.ImportSpec, len(f.Imports)) for i, im := range f.Imports { - importPath, err := strconv.Unquote(im.Path.Value) + _, err := strconv.Unquote(im.Path.Value) if err != nil { // should not happen - parser.ParseFile should already ensure we get // a valid string literal here. panic(fmt.Errorf("%v: unexpected invalid import path: %v", fset.Position(im.Pos()).String(), im.Path.Value)) } - res[i] = FileImport{ - PkgPath: importPath, - Spec: im, - } + res[i] = im } return res, nil } -type ImportsMap map[FileKind][]FileImport +type ImportsMap map[FileKind][]string + +func ImportsMapFromSpecs(specs ImportsSpecsMap, fset *token.FileSet) ImportsMap { + res := make(ImportsMap, len(specs)) + for k, v := range specs { + c := make([]string, 0, len(v)) + for _, x := range v { + pkgPath, err := strconv.Unquote(x.Path.Value) + if err != nil { + // should not happen - parser.ParseFile should already ensure we get + // a valid string literal here. + panic(fmt.Errorf("%v: unexpected invalid import path: %v", fset.Position(x.Pos()).String(), x.Path.Value)) + } + c = append(c, pkgPath) + } + res[k] = c + } + return res +} // Merge merges imports, it removes duplicates and sorts the result -func (imap ImportsMap) Merge(kinds ...FileKind) []FileImport { - res := make([]FileImport, 0, 16) +func (imap ImportsMap) Merge(kinds ...FileKind) []string { + res := make([]string, 0, 16) seen := make(map[string]struct{}, 16) for _, kind := range kinds { for _, im := range imap[kind] { - if _, ok := seen[im.PkgPath]; ok { + if _, ok := seen[im]; ok { continue } - seen[im.PkgPath] = struct{}{} + seen[im] = struct{}{} res = append(res, im) } @@ -105,8 +120,36 @@ func (imap ImportsMap) Merge(kinds ...FileKind) []FileImport { return res } -func sortImports(imports []FileImport) { +type ImportsSpecsMap map[FileKind][]*ast.ImportSpec + +// Merge merges imports, it removes duplicates and sorts the result +func (imap ImportsSpecsMap) Merge(kinds ...FileKind) []*ast.ImportSpec { + res := make([]*ast.ImportSpec, 0, 16) + seen := make(map[string]struct{}, 16) + + for _, kind := range kinds { + for _, im := range imap[kind] { + if _, ok := seen[im.Path.Value]; ok { + continue + } + seen[im.Path.Value] = struct{}{} + + res = append(res, im) + } + } + + sortImportsSpecs(res) + return res +} + +func sortImports(imports []string) { + sort.Slice(imports, func(i, j int) bool { + return imports[i] < imports[j] + }) +} + +func sortImportsSpecs(imports []*ast.ImportSpec) { sort.Slice(imports, func(i, j int) bool { - return imports[i].PkgPath < imports[j].PkgPath + return imports[i].Path.Value < imports[j].Path.Value }) } diff --git a/gnovm/pkg/packages/imports_test.go b/gnovm/pkg/packages/imports_test.go index f9f58b967c8..08892fe413b 100644 --- a/gnovm/pkg/packages/imports_test.go +++ b/gnovm/pkg/packages/imports_test.go @@ -1,6 +1,7 @@ package packages import ( + "go/token" "os" "path/filepath" "testing" @@ -112,7 +113,7 @@ func TestImports(t *testing.T) { // - ignore subdirs // - ignore duplicate // - should be sorted - expected := map[FileKind][]string{ + expected := ImportsMap{ FileKindPackageSource: { "gno.land/p/demo/pkg1", "gno.land/p/demo/pkg2", @@ -145,18 +146,13 @@ func TestImports(t *testing.T) { pkg, err := gnolang.ReadMemPackage(tmpDir, "test") require.NoError(t, err) - importsMap, err := Imports(pkg, nil) + fset := token.NewFileSet() + + importsSpec, err := ImportsSpecs(pkg, nil) require.NoError(t, err) // ignore specs - got := map[FileKind][]string{} - for key, vals := range importsMap { - stringVals := make([]string, len(vals)) - for i, val := range vals { - stringVals[i] = val.PkgPath - } - got[key] = stringVals - } + got := ImportsMapFromSpecs(importsSpec, fset) require.Equal(t, expected, got) } diff --git a/gnovm/pkg/packages/load.go b/gnovm/pkg/packages/load.go new file mode 100644 index 00000000000..b82e58d7ced --- /dev/null +++ b/gnovm/pkg/packages/load.go @@ -0,0 +1,223 @@ +package packages + +import ( + "errors" + "fmt" + "go/token" + "os" + "path/filepath" + + "github.com/gnolang/gno/gnovm" + "github.com/gnolang/gno/gnovm/pkg/gnoenv" + "github.com/gnolang/gno/gnovm/pkg/gnolang" + "github.com/gnolang/gno/gnovm/pkg/gnomod" + "github.com/gnolang/gno/gnovm/pkg/packages/pkgdownload" + "github.com/gnolang/gno/gnovm/pkg/packages/pkgdownload/rpcpkgfetcher" + "github.com/gnolang/gno/tm2/pkg/commands" + "golang.org/x/mod/module" +) + +type LoadConfig struct { + IO commands.IO + Fetcher pkgdownload.PackageFetcher + Deps bool + Cache PackagesMap + SelfContained bool + AllowEmpty bool + DepsPatterns []string +} + +func (conf *LoadConfig) applyDefaults() { + if conf.IO == nil { + conf.IO = commands.NewTestIO() + } + if conf.Fetcher == nil { + conf.Fetcher = rpcpkgfetcher.New(nil) + } + if conf.Cache == nil { + conf.Cache = map[string]*Package{} + } +} + +func Load(conf *LoadConfig, patterns ...string) (PkgList, error) { + conf.applyDefaults() + + fset := token.NewFileSet() + + expanded, err := expandPatterns(conf, patterns...) + if err != nil { + return nil, err + } + + pkgs, err := readPackages(expanded, fset) + if err != nil { + return nil, err + } + + if !conf.AllowEmpty { + if len(pkgs) == 0 { + return nil, errors.New("no packages") + } + } + + if !conf.Deps { + return pkgs, nil + } + + extra, err := expandPatterns(conf, conf.DepsPatterns...) + if err != nil { + return nil, err + } + for _, m := range extra { + m.Match = []string{} + } + + extraPkgs, err := readPackages(extra, fset) + if err != nil { + return nil, err + } + extraMap := NewPackagesMap(extraPkgs...) + + gnoroot := gnoenv.RootDir() + + toVisit := pkgs + queuedByPkgPath := NewPackagesMap(pkgs...) + markForVisit := func(pkg *Package) { + queuedByPkgPath.Add(pkg) + toVisit = append(toVisit, pkg) + } + + visited := map[string]struct{}{} + loaded := []*Package{} + + for { + pkg, ok := fifoNext(&toVisit) + if !ok { + break + } + + if added := setAdd(visited, pkg.Dir); !added { + continue + } + + for _, imp := range pkg.Imports.Merge(FileKindPackageSource, FileKindTest, FileKindXTest, FileKindFiletest) { + // check if we already queued this dep + if _, ok := queuedByPkgPath[imp]; ok { + continue + } + + // check if we have it in config cache + if cached, ok := conf.Cache[imp]; ok { + markForVisit(cached) + continue + } + + // check if we have it in extra deps patterns + if extra, ok := extraMap[imp]; ok { + markForVisit(extra) + continue + } + + // check if this is a stdlib and queue it + if gnolang.IsStdlib(imp) { + pkg := readPkgDir(filepath.Join(gnoroot, "gnovm", "stdlibs", filepath.FromSlash(imp)), imp, fset) + markForVisit(pkg) + continue + } + + if conf.SelfContained { + pkg.Errors = append(pkg.Errors, fmt.Errorf("self-contained: package %q not found", imp)) + delete(queuedByPkgPath, imp) // stop trying to get this lib, we can't + continue + } + + dir := gnomod.PackageDir("", module.Version{Path: imp}) + if err := downloadPackage(conf, imp, dir); err != nil { + pkg.Errors = append(pkg.Errors, err) + delete(queuedByPkgPath, imp) // stop trying to get this lib, we can't + continue + } + markForVisit(readPkgDir(dir, imp, fset)) + } + + loaded = append(loaded, pkg) + } + + for _, pkg := range loaded { + // TODO: this could be optimized + pkg.Deps, err = listDeps(pkg.ImportPath, queuedByPkgPath) + if err != nil { + pkg.Errors = append(pkg.Errors, err) + } + } + + return loaded, nil +} + +func listDeps(target string, pkgs map[string]*Package) ([]string, error) { + deps := []string{} + err := listDepsRecursive(target, target, pkgs, &deps, make(map[string]struct{})) + return deps, err +} + +func listDepsRecursive(rootTarget string, target string, pkgs map[string]*Package, deps *[]string, visited map[string]struct{}) error { + if _, ok := visited[target]; ok { + return nil + } + pkg := pkgs[target] + if pkg == nil { + return fmt.Errorf("package %s not found", target) + } + visited[target] = struct{}{} + var outErr error + for _, imp := range pkg.Imports.Merge(FileKindPackageSource, FileKindTest, FileKindXTest, FileKindFiletest) { + err := listDepsRecursive(rootTarget, imp, pkgs, deps, visited) + if err != nil { + outErr = errors.Join(outErr, err) + } + } + if target != rootTarget { + (*deps) = append(*deps, target) + } + return outErr +} + +func (p *Package) MemPkg() (*gnovm.MemPackage, error) { + files := []*gnovm.MemFile{} + for _, cat := range p.Files { + for _, f := range cat { + body, err := os.ReadFile(filepath.Join(p.Dir, f)) + if err != nil { + return nil, err + } + files = append(files, &gnovm.MemFile{ + Name: f, + Body: string(body), + }) + } + } + return &gnovm.MemPackage{ + Name: p.Name, + Path: p.ImportPath, + Files: files, + }, nil +} + +func fifoNext[T any](slice *[]T) (T, bool) { + if len(*slice) == 0 { + return *new(T), false + } + + elem := (*slice)[0] + *slice = (*slice)[1:] + return elem, true +} + +func setAdd[T comparable](set map[T]struct{}, val T) bool { + if _, ok := set[val]; ok { + return false + } + + set[val] = struct{}{} + return true +} diff --git a/gnovm/pkg/gnomod/pkg_test.go b/gnovm/pkg/packages/load_test.go similarity index 68% rename from gnovm/pkg/gnomod/pkg_test.go rename to gnovm/pkg/packages/load_test.go index 7c3035a4b7b..72bcdd1d6c9 100644 --- a/gnovm/pkg/gnomod/pkg_test.go +++ b/gnovm/pkg/packages/load_test.go @@ -1,10 +1,11 @@ -package gnomod +package packages import ( "os" "path/filepath" "testing" + "github.com/gnolang/gno/gnovm/pkg/packages/pkgdownload/examplespkgfetcher" "github.com/gnolang/gno/tm2/pkg/testutils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -74,11 +75,11 @@ func TestListAndNonDraftPkgs(t *testing.T) { } // List packages - pkgs, err := ListPkgs(dirPath) + pkgs, err := Load(&LoadConfig{AllowEmpty: true, Fetcher: examplespkgfetcher.New()}, filepath.Join(dirPath, "...")) require.NoError(t, err) assert.Equal(t, len(tc.outListPkgs), len(pkgs)) for _, p := range pkgs { - assert.Contains(t, tc.outListPkgs, p.Name) + assert.Contains(t, tc.outListPkgs, p.ImportPath) } // Sort packages @@ -89,7 +90,7 @@ func TestListAndNonDraftPkgs(t *testing.T) { nonDraft := sorted.GetNonDraftPkgs() assert.Equal(t, len(tc.outNonDraftPkgs), len(nonDraft)) for _, p := range nonDraft { - assert.Contains(t, tc.outNonDraftPkgs, p.Name) + assert.Contains(t, tc.outNonDraftPkgs, p.ImportPath) } }) } @@ -117,35 +118,35 @@ func TestSortPkgs(t *testing.T) { }{ { desc: "empty_input", - in: []Pkg{}, + in: []*Package{}, expected: make([]string, 0), }, { desc: "no_dependencies", - in: []Pkg{ - {Name: "pkg1", Dir: "/path/to/pkg1", Imports: []string{}}, - {Name: "pkg2", Dir: "/path/to/pkg2", Imports: []string{}}, - {Name: "pkg3", Dir: "/path/to/pkg3", Imports: []string{}}, + in: []*Package{ + {ImportPath: "pkg1", Dir: "/path/to/pkg1", Imports: ImportsMap{}}, + {ImportPath: "pkg2", Dir: "/path/to/pkg2", Imports: ImportsMap{}}, + {ImportPath: "pkg3", Dir: "/path/to/pkg3", Imports: ImportsMap{}}, }, expected: []string{"pkg1", "pkg2", "pkg3"}, }, { desc: "circular_dependencies", - in: []Pkg{ - {Name: "pkg1", Dir: "/path/to/pkg1", Imports: []string{"pkg2"}}, - {Name: "pkg2", Dir: "/path/to/pkg2", Imports: []string{"pkg1"}}, + in: []*Package{ + {ImportPath: "pkg1", Dir: "/path/to/pkg1", Imports: ImportsMap{FileKindPackageSource: []string{"pkg2"}}}, + {ImportPath: "pkg2", Dir: "/path/to/pkg2", Imports: ImportsMap{FileKindPackageSource: []string{"pkg1"}}}, }, shouldErr: true, }, { desc: "missing_dependencies", - in: []Pkg{ - {Name: "pkg1", Dir: "/path/to/pkg1", Imports: []string{"pkg2"}}, + in: []*Package{ + {ImportPath: "pkg1", Dir: "/path/to/pkg1", Imports: ImportsMap{FileKindPackageSource: []string{"pkg2"}}}, }, shouldErr: true, }, { desc: "valid_dependencies", - in: []Pkg{ - {Name: "pkg1", Dir: "/path/to/pkg1", Imports: []string{"pkg2"}}, - {Name: "pkg2", Dir: "/path/to/pkg2", Imports: []string{"pkg3"}}, - {Name: "pkg3", Dir: "/path/to/pkg3", Imports: []string{}}, + in: []*Package{ + {ImportPath: "pkg1", Dir: "/path/to/pkg1", Imports: ImportsMap{FileKindPackageSource: []string{"pkg2"}}}, + {ImportPath: "pkg2", Dir: "/path/to/pkg2", Imports: ImportsMap{FileKindPackageSource: []string{"pkg3"}}}, + {ImportPath: "pkg3", Dir: "/path/to/pkg3", Imports: ImportsMap{}}, }, expected: []string{"pkg3", "pkg2", "pkg1"}, }, @@ -157,7 +158,7 @@ func TestSortPkgs(t *testing.T) { } else { require.NoError(t, err) for i := range tc.expected { - assert.Equal(t, tc.expected[i], sorted[i].Name) + assert.Equal(t, tc.expected[i], sorted[i].ImportPath) } } }) diff --git a/gnovm/pkg/packages/patterns.go b/gnovm/pkg/packages/patterns.go new file mode 100644 index 00000000000..220101694c9 --- /dev/null +++ b/gnovm/pkg/packages/patterns.go @@ -0,0 +1,27 @@ +package packages + +import ( + "strings" +) + +// patternIsRemote reports wether a pattern is starting with a domain +func patternIsRemote(path string) bool { + if len(path) == 0 { + return false + } + if path[0] == '.' { + return false + } + slashIdx := strings.IndexRune(path, '/') + if slashIdx == -1 { + return false + } + return strings.ContainsRune(path[:slashIdx], '.') +} + +// PatternIsLiteral reports whether the pattern is free of wildcards. +// +// A literal pattern must match at most one package. +func PatternIsLiteral(pattern string) bool { + return !strings.Contains(pattern, "...") +} diff --git a/gnovm/cmd/gno/internal/pkgdownload/examplespkgfetcher/examplespkgfetcher.go b/gnovm/pkg/packages/pkgdownload/examplespkgfetcher/examplespkgfetcher.go similarity index 95% rename from gnovm/cmd/gno/internal/pkgdownload/examplespkgfetcher/examplespkgfetcher.go rename to gnovm/pkg/packages/pkgdownload/examplespkgfetcher/examplespkgfetcher.go index 1642c62d21e..56b567e9525 100644 --- a/gnovm/cmd/gno/internal/pkgdownload/examplespkgfetcher/examplespkgfetcher.go +++ b/gnovm/pkg/packages/pkgdownload/examplespkgfetcher/examplespkgfetcher.go @@ -8,8 +8,8 @@ import ( "path/filepath" "github.com/gnolang/gno/gnovm" - "github.com/gnolang/gno/gnovm/cmd/gno/internal/pkgdownload" "github.com/gnolang/gno/gnovm/pkg/gnoenv" + "github.com/gnolang/gno/gnovm/pkg/packages/pkgdownload" ) type ExamplesPackageFetcher struct{} diff --git a/gnovm/cmd/gno/internal/pkgdownload/pkgdownload.go b/gnovm/pkg/packages/pkgdownload/pkgdownload.go similarity index 100% rename from gnovm/cmd/gno/internal/pkgdownload/pkgdownload.go rename to gnovm/pkg/packages/pkgdownload/pkgdownload.go diff --git a/gnovm/cmd/gno/internal/pkgdownload/pkgfetcher.go b/gnovm/pkg/packages/pkgdownload/pkgfetcher.go similarity index 100% rename from gnovm/cmd/gno/internal/pkgdownload/pkgfetcher.go rename to gnovm/pkg/packages/pkgdownload/pkgfetcher.go diff --git a/gnovm/cmd/gno/internal/pkgdownload/rpcpkgfetcher/rpcpkgfetcher.go b/gnovm/pkg/packages/pkgdownload/rpcpkgfetcher/rpcpkgfetcher.go similarity index 97% rename from gnovm/cmd/gno/internal/pkgdownload/rpcpkgfetcher/rpcpkgfetcher.go rename to gnovm/pkg/packages/pkgdownload/rpcpkgfetcher/rpcpkgfetcher.go index a71c1d43719..ac219aeb387 100644 --- a/gnovm/cmd/gno/internal/pkgdownload/rpcpkgfetcher/rpcpkgfetcher.go +++ b/gnovm/pkg/packages/pkgdownload/rpcpkgfetcher/rpcpkgfetcher.go @@ -8,7 +8,7 @@ import ( "strings" "github.com/gnolang/gno/gnovm" - "github.com/gnolang/gno/gnovm/cmd/gno/internal/pkgdownload" + "github.com/gnolang/gno/gnovm/pkg/packages/pkgdownload" "github.com/gnolang/gno/tm2/pkg/bft/rpc/client" ) diff --git a/gnovm/cmd/gno/internal/pkgdownload/rpcpkgfetcher/rpcpkgfetcher_test.go b/gnovm/pkg/packages/pkgdownload/rpcpkgfetcher/rpcpkgfetcher_test.go similarity index 100% rename from gnovm/cmd/gno/internal/pkgdownload/rpcpkgfetcher/rpcpkgfetcher_test.go rename to gnovm/pkg/packages/pkgdownload/rpcpkgfetcher/rpcpkgfetcher_test.go diff --git a/gnovm/pkg/packages/pkglist.go b/gnovm/pkg/packages/pkglist.go new file mode 100644 index 00000000000..a0c0106c20b --- /dev/null +++ b/gnovm/pkg/packages/pkglist.go @@ -0,0 +1,114 @@ +package packages + +import ( + "fmt" +) + +type ( + PkgList []*Package + SortedPkgList []*Package +) + +// sortPkgs sorts the given packages by their dependencies. +func (pl PkgList) Sort() (SortedPkgList, error) { + visited := make(map[string]bool) + onStack := make(map[string]bool) + sortedPkgs := make([]*Package, 0, len(pl)) + + // Visit all packages + for _, p := range pl { + if err := visitPackage(p, pl, visited, onStack, &sortedPkgs); err != nil { + return nil, err + } + } + + return sortedPkgs, nil +} + +// visitNode visits a package's and its dependencies dependencies and adds them to the sorted list. +func visitPackage(pkg *Package, pkgs []*Package, visited, onStack map[string]bool, sortedPkgs *[]*Package) error { + if onStack[pkg.ImportPath] { + return fmt.Errorf("cycle detected: %s", pkg.ImportPath) + } + if visited[pkg.ImportPath] { + return nil + } + + visited[pkg.ImportPath] = true + onStack[pkg.ImportPath] = true + + // Visit package's dependencies + for _, imp := range pkg.Imports.Merge(FileKindPackageSource) { + found := false + for _, p := range pkgs { + if p.ImportPath != imp { + continue + } + if err := visitPackage(p, pkgs, visited, onStack, sortedPkgs); err != nil { + return err + } + found = true + break + } + if !found { + return fmt.Errorf("missing dependency '%s' for package '%s'", imp, pkg.ImportPath) + } + } + + onStack[pkg.ImportPath] = false + *sortedPkgs = append(*sortedPkgs, pkg) + return nil +} + +// GetNonDraftPkgs returns packages that are not draft +// and have no direct or indirect draft dependencies. +func (sp SortedPkgList) GetNonDraftPkgs() SortedPkgList { + res := make([]*Package, 0, len(sp)) + draft := make(map[string]bool) + + for _, pkg := range sp { + if pkg.Draft { + draft[pkg.ImportPath] = true + continue + } + dependsOnDraft := false + for _, req := range pkg.Imports.Merge(FileKindPackageSource) { + if draft[req] { + dependsOnDraft = true + draft[pkg.ImportPath] = true + break + } + } + if !dependsOnDraft { + res = append(res, pkg) + } + } + return res +} + +type PackagesMap map[string]*Package + +func NewPackagesMap(pkgs ...*Package) PackagesMap { + pm := make(PackagesMap, len(pkgs)) + pm.AddBulk(pkgs...) + return pm +} + +func (pm PackagesMap) Add(pkg *Package) bool { + if pkg.ImportPath == "" { + return false + } + + if _, ok := pm[pkg.ImportPath]; ok { + return false + } + + pm[pkg.ImportPath] = pkg + return true +} + +func (pm PackagesMap) AddBulk(pkgs ...*Package) { + for _, pkg := range pkgs { + _ = pm.Add(pkg) + } +} diff --git a/gnovm/pkg/packages/types.go b/gnovm/pkg/packages/types.go new file mode 100644 index 00000000000..e2988341301 --- /dev/null +++ b/gnovm/pkg/packages/types.go @@ -0,0 +1,58 @@ +package packages + +import "sort" + +// ported from https://cs.opensource.google/go/go/+/refs/tags/go1.23.2:src/cmd/go/internal/load/pkg.go +type Package struct { + Dir string `json:",omitempty"` // directory containing package sources + ImportPath string `json:",omitempty"` // import path of package in dir + Name string `json:",omitempty"` // package name + Root string `json:",omitempty"` // Gno root, Gno path dir, or module root dir containing this package + ModPath string + Match []string `json:",omitempty"` // command-line patterns matching this package + Errors []error `json:",omitempty"` // error loading this package (not dependencies) + Draft bool + Files FilesMap + Imports ImportsMap `json:",omitempty"` // import paths used by this package + Deps []string `json:",omitempty"` // all (recursively) imported dependencies +} + +type FilesMap map[FileKind][]string + +func (fm FilesMap) Size() int { + total := 0 + for _, kind := range AllFileKinds() { + total += len(fm[kind]) + } + return total +} + +// Merge merges imports, it removes duplicates and sorts the result +func (fm FilesMap) Merge(kinds ...FileKind) []string { + res := make([]string, 0, 16) + + for _, kind := range kinds { + res = append(res, fm[kind]...) + } + + sortPaths(res) + return res +} + +func sortPaths(imports []string) { + sort.Slice(imports, func(i, j int) bool { + return imports[i] < imports[j] + }) +} + +func Inject(pkgsMap map[string]*Package, pkgs []*Package) { + for _, pkg := range pkgs { + if pkg.ImportPath == "" { + continue + } + if _, ok := pkgsMap[pkg.ImportPath]; ok { + continue + } + pkgsMap[pkg.ImportPath] = pkg + } +} diff --git a/gnovm/pkg/repl/repl.go b/gnovm/pkg/repl/repl.go index b0944d21646..977b664e854 100644 --- a/gnovm/pkg/repl/repl.go +++ b/gnovm/pkg/repl/repl.go @@ -125,7 +125,7 @@ func NewRepl(opts ...ReplOption) *Repl { r.stderr = &b r.storeFunc = func() gno.Store { - _, st := test.Store(gnoenv.RootDir(), false, r.stdin, r.stdout, r.stderr) + _, st := test.Store(gnoenv.RootDir(), nil, false, r.stdin, r.stdout, r.stderr) return st } diff --git a/gnovm/pkg/test/imports.go b/gnovm/pkg/test/imports.go index a8dd709e501..8b96400ead8 100644 --- a/gnovm/pkg/test/imports.go +++ b/gnovm/pkg/test/imports.go @@ -10,6 +10,7 @@ import ( "os" "path/filepath" "runtime/debug" + "strconv" "strings" "time" @@ -19,7 +20,6 @@ import ( teststdlibs "github.com/gnolang/gno/gnovm/tests/stdlibs" teststd "github.com/gnolang/gno/gnovm/tests/stdlibs/std" "github.com/gnolang/gno/tm2/pkg/db/memdb" - osm "github.com/gnolang/gno/tm2/pkg/os" "github.com/gnolang/gno/tm2/pkg/std" "github.com/gnolang/gno/tm2/pkg/store/dbadapter" storetypes "github.com/gnolang/gno/tm2/pkg/store/types" @@ -28,6 +28,7 @@ import ( // NOTE: this isn't safe, should only be used for testing. func Store( rootDir string, + pkgs map[string]*packages.Package, withExtern bool, stdin io.Reader, stdout, stderr io.Writer, @@ -36,6 +37,8 @@ func Store( resStore gno.Store, ) { getPackage := func(pkgPath string, store gno.Store) (pn *gno.PackageNode, pv *gno.PackageValue) { + // fmt.Println("getting pkg", pkgPath) + if pkgPath == "" { panic(fmt.Sprintf("invalid zero package path in testStore().pkgGetter")) } @@ -134,14 +137,15 @@ func Store( return } - // if examples package... - examplePath := filepath.Join(rootDir, "examples", pkgPath) - if osm.DirExists(examplePath) { - memPkg := gno.MustReadMemPackage(examplePath, pkgPath) + // if known package + if pkg, ok := pkgs[pkgPath]; ok { + memPkg := gno.MustReadMemPackage(pkg.Dir, pkgPath) if memPkg.IsEmpty() { panic(fmt.Sprintf("found an empty package %q", pkgPath)) } + // fmt.Println("loading", pkgPath, "from", pkg.Dir, "err", pkg.Error) + send := std.Coins{} ctx := Context(pkgPath, send) m2 := gno.NewMachineWithOptions(gno.MachineOptions{ @@ -153,6 +157,7 @@ func Store( pn, pv = m2.RunMemPackage(memPkg, true) return } + return nil, nil } db := memdb.NewMemDB() @@ -242,20 +247,26 @@ func LoadImports(store gno.Store, memPkg *gnovm.MemPackage) (err error) { }() fset := token.NewFileSet() - importsMap, err := packages.Imports(memPkg, fset) + importsMap, err := packages.ImportsSpecs(memPkg, fset) if err != nil { return err } imports := importsMap.Merge(packages.FileKindPackageSource, packages.FileKindTest, packages.FileKindXTest) - for _, imp := range imports { - if gno.IsRealmPath(imp.PkgPath) { + for _, im := range imports { + pkgPath, err := strconv.Unquote(im.Path.Value) + if err != nil { + // should not happen - parser.ParseFile should already ensure we get + // a valid string literal here. + panic(fmt.Errorf("%v: unexpected invalid import path: %v", fset.Position(im.Pos()).String(), im.Path.Value)) + } + if gno.IsRealmPath(pkgPath) { // Don't eagerly load realms. // Realms persist state and can change the state of other realms in initialization. continue } - pkg := store.GetPackage(imp.PkgPath, true) + pkg := store.GetPackage(pkgPath, true) if pkg == nil { - return fmt.Errorf("%v: unknown import path %v", fset.Position(imp.Spec.Pos()).String(), imp.PkgPath) + return fmt.Errorf("%s: unknown import path %s", fset.Position(im.Pos()).String(), pkgPath) } } return nil diff --git a/gnovm/pkg/test/test.go b/gnovm/pkg/test/test.go index d06540761d7..5d1a306c44f 100644 --- a/gnovm/pkg/test/test.go +++ b/gnovm/pkg/test/test.go @@ -17,6 +17,7 @@ import ( "github.com/gnolang/gno/gnovm" gno "github.com/gnolang/gno/gnovm/pkg/gnolang" + "github.com/gnolang/gno/gnovm/pkg/packages" "github.com/gnolang/gno/gnovm/stdlibs" teststd "github.com/gnolang/gno/gnovm/tests/stdlibs/std" "github.com/gnolang/gno/tm2/pkg/crypto" @@ -133,14 +134,14 @@ func (opts *TestOptions) WriterForStore() io.Writer { } // NewTestOptions sets up TestOptions, filling out all "required" parameters. -func NewTestOptions(rootDir string, stdin io.Reader, stdout, stderr io.Writer) *TestOptions { +func NewTestOptions(rootDir string, pkgs map[string]*packages.Package, stdin io.Reader, stdout, stderr io.Writer) *TestOptions { opts := &TestOptions{ RootDir: rootDir, Output: stdout, Error: stderr, } opts.BaseStore, opts.TestStore = Store( - rootDir, false, + rootDir, pkgs, false, stdin, opts.WriterForStore(), stderr, ) return opts @@ -181,11 +182,15 @@ func Test(memPkg *gnovm.MemPackage, fsDir string, opts *TestOptions) error { var errs error + // fmt.Println("loading imports for", memPkg.Path, fsDir) + // Eagerly load imports. if err := LoadImports(opts.TestStore, memPkg); err != nil { return err } + // fmt.Println("loaded imports for", memPkg.Path, fsDir) + // Stands for "test", "integration test", and "filetest". // "integration test" are the test files with `package xxx_test` (they are // not necessarily integration tests, it's just for our internal reference.) diff --git a/gnovm/pkg/transpiler/transpiler.go b/gnovm/pkg/transpiler/transpiler.go index bd4bb1b1bc9..b83ef867641 100644 --- a/gnovm/pkg/transpiler/transpiler.go +++ b/gnovm/pkg/transpiler/transpiler.go @@ -227,7 +227,7 @@ func (ctx *transpileCtx) transformCallExpr(_ *astutil.Cursor, ce *ast.CallExpr) // its replacement is a type with the method AssertOriginCall, this system // will incorrectly add a `nil` as the first argument. // A full fix requires understanding scope; the Go standard library recommends - // using go/types, which for proper functioning requires an importer + // using go/types, which for proper functioning requires an packages // which can work with Gno. This is deferred for a future PR. id, ok := fe.X.(*ast.Ident) if !ok { diff --git a/gnovm/stdlibs/crypto/ed25519/ed25519_test.gno b/gnovm/stdlibs/crypto/ed25519/ed25519_test.gno index 615ea0eb6a7..db0cd1b9c1a 100644 --- a/gnovm/stdlibs/crypto/ed25519/ed25519_test.gno +++ b/gnovm/stdlibs/crypto/ed25519/ed25519_test.gno @@ -1,4 +1,4 @@ -package ed25519 +package ed25519_test import ( "crypto/ed25519" diff --git a/gnovm/stdlibs/crypto/sha256/sha256_test.gno b/gnovm/stdlibs/crypto/sha256/sha256_test.gno index 26d96cd547e..3a443b0cae3 100644 --- a/gnovm/stdlibs/crypto/sha256/sha256_test.gno +++ b/gnovm/stdlibs/crypto/sha256/sha256_test.gno @@ -1,4 +1,4 @@ -package sha256 +package sha256_test import ( "crypto/sha256" diff --git a/gnovm/stdlibs/encoding/binary/binary_test.gno b/gnovm/stdlibs/encoding/binary/binary_test.gno index 5407eb5061b..7905c381189 100644 --- a/gnovm/stdlibs/encoding/binary/binary_test.gno +++ b/gnovm/stdlibs/encoding/binary/binary_test.gno @@ -1,4 +1,4 @@ -package binary +package binary_test import ( "bytes" diff --git a/gnovm/stdlibs/generated.go b/gnovm/stdlibs/generated.go index d5ab052028f..5201d1d6fe4 100644 --- a/gnovm/stdlibs/generated.go +++ b/gnovm/stdlibs/generated.go @@ -900,6 +900,44 @@ var nativeFuncs = [...]NativeFunc{ )) }, }, + { + "testing", + "matchString", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("string")}, + {Name: gno.N("p1"), Type: gno.X("string")}, + }, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("bool")}, + {Name: gno.N("r1"), Type: gno.X("error")}, + }, + false, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 string + rp0 = reflect.ValueOf(&p0).Elem() + p1 string + rp1 = reflect.ValueOf(&p1).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 1, "")).TV, rp1) + + r0, r1 := libs_testing.X_matchString(p0, p1) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r1).Elem(), + )) + }, + }, { "time", "now", diff --git a/gnovm/stdlibs/hash/marshal_test.gno b/gnovm/stdlibs/hash/marshal_test.gno index bf823d97cce..d15c02e87b5 100644 --- a/gnovm/stdlibs/hash/marshal_test.gno +++ b/gnovm/stdlibs/hash/marshal_test.gno @@ -6,7 +6,7 @@ // BinaryMarshaler, BinaryUnmarshaler, // and lock in the current representations. -package hash +package hash_test import ( "bytes" diff --git a/gnovm/stdlibs/io/export_test.gno b/gnovm/stdlibs/io/export_test.gno new file mode 100644 index 00000000000..06853f975f5 --- /dev/null +++ b/gnovm/stdlibs/io/export_test.gno @@ -0,0 +1,10 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package io + +// exported for test +var ErrInvalidWrite = errInvalidWrite +var ErrWhence = errWhence +var ErrOffset = errOffset diff --git a/gnovm/stdlibs/io/io_test.gno b/gnovm/stdlibs/io/io_test.gno index 4915982057b..38e535b3cfb 100644 --- a/gnovm/stdlibs/io/io_test.gno +++ b/gnovm/stdlibs/io/io_test.gno @@ -1,4 +1,4 @@ -package io +package io_test // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -8,6 +8,7 @@ import ( "bytes" "errors" "fmt" + "io" "strings" "testing" ) @@ -15,8 +16,8 @@ import ( // A version of bytes.Buffer without ReadFrom and WriteTo type Buffer struct { bytes.Buffer - ReaderFrom // conflicts with and hides bytes.Buffer's ReaderFrom. - WriterTo // conflicts with and hides bytes.Buffer's WriterTo. + io.ReaderFrom // conflicts with and hides bytes.Buffer's ReaderFrom. + io.WriterTo // conflicts with and hides bytes.Buffer's WriterTo. } // Simple tests, primarily to verify the ReadFrom and WriteTo callouts inside Copy, CopyBuffer and CopyN. @@ -25,7 +26,7 @@ func TestCopy(t *testing.T) { rb := new(Buffer) wb := new(Buffer) rb.WriteString("hello, world.") - Copy(wb, rb) + io.Copy(wb, rb) if wb.String() != "hello, world." { t.Errorf("Copy did not work properly") } @@ -35,12 +36,12 @@ func TestCopyNegative(t *testing.T) { rb := new(Buffer) wb := new(Buffer) rb.WriteString("hello") - Copy(wb, &LimitedReader{R: rb, N: -1}) + io.Copy(wb, &io.LimitedReader{R: rb, N: -1}) if wb.String() != "" { t.Errorf("Copy on LimitedReader with N<0 copied data") } - CopyN(wb, rb, -1) + io.CopyN(wb, rb, -1) if wb.String() != "" { t.Errorf("CopyN with N<0 copied data") } @@ -50,7 +51,7 @@ func TestCopyBuffer(t *testing.T) { rb := new(Buffer) wb := new(Buffer) rb.WriteString("hello, world.") - CopyBuffer(wb, rb, make([]byte, 1)) // Tiny buffer to keep it honest. + io.CopyBuffer(wb, rb, make([]byte, 1)) // Tiny buffer to keep it honest. if wb.String() != "hello, world." { t.Errorf("CopyBuffer did not work properly") } @@ -60,7 +61,7 @@ func TestCopyBufferNil(t *testing.T) { rb := new(Buffer) wb := new(Buffer) rb.WriteString("hello, world.") - CopyBuffer(wb, rb, nil) // Should allocate a buffer. + io.CopyBuffer(wb, rb, nil) // Should allocate a buffer. if wb.String() != "hello, world." { t.Errorf("CopyBuffer did not work properly") } @@ -70,7 +71,7 @@ func TestCopyReadFrom(t *testing.T) { rb := new(Buffer) wb := new(bytes.Buffer) // implements ReadFrom. rb.WriteString("hello, world.") - Copy(wb, rb) + io.Copy(wb, rb) if wb.String() != "hello, world." { t.Errorf("Copy did not work properly") } @@ -80,7 +81,7 @@ func TestCopyWriteTo(t *testing.T) { rb := new(bytes.Buffer) // implements WriteTo. wb := new(Buffer) rb.WriteString("hello, world.") - Copy(wb, rb) + io.Copy(wb, rb) if wb.String() != "hello, world." { t.Errorf("Copy did not work properly") } @@ -92,7 +93,7 @@ type writeToChecker struct { writeToCalled bool } -func (wt *writeToChecker) WriteTo(w Writer) (int64, error) { +func (wt *writeToChecker) WriteTo(w io.Writer) (int64, error) { wt.writeToCalled = true return wt.Buffer.WriteTo(w) } @@ -104,7 +105,7 @@ func TestCopyPriority(t *testing.T) { rb := new(writeToChecker) wb := new(bytes.Buffer) rb.WriteString("hello, world.") - Copy(wb, rb) + io.Copy(wb, rb) if wb.String() != "hello, world." { t.Errorf("Copy did not work properly") } else if !rb.writeToCalled { @@ -134,7 +135,7 @@ func (w errWriter) Write([]byte) (int, error) { func TestCopyReadErrWriteErr(t *testing.T) { er, ew := errors.New("readError"), errors.New("writeError") r, w := zeroErrReader{err: er}, errWriter{err: ew} - n, err := Copy(w, r) + n, err := io.Copy(w, r) if n != 0 || err != ew { t.Errorf("Copy(zeroErrReader, errWriter) = %d, %v; want 0, writeError", n, err) } @@ -144,7 +145,7 @@ func TestCopyN(t *testing.T) { rb := new(Buffer) wb := new(Buffer) rb.WriteString("hello, world.") - CopyN(wb, rb, 5) + io.CopyN(wb, rb, 5) if wb.String() != "hello" { t.Errorf("CopyN did not work properly") } @@ -154,7 +155,7 @@ func TestCopyNReadFrom(t *testing.T) { rb := new(Buffer) wb := new(bytes.Buffer) // implements ReadFrom. rb.WriteString("hello") - CopyN(wb, rb, 5) + io.CopyN(wb, rb, 5) if wb.String() != "hello" { t.Errorf("CopyN did not work properly") } @@ -164,7 +165,7 @@ func TestCopyNWriteTo(t *testing.T) { rb := new(bytes.Buffer) // implements WriteTo. wb := new(Buffer) rb.WriteString("hello, world.") - CopyN(wb, rb, 5) + io.CopyN(wb, rb, 5) if wb.String() != "hello" { t.Errorf("CopyN did not work properly") } @@ -177,7 +178,7 @@ func BenchmarkCopyNSmall(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - CopyN(buf, rd, 512) + io.CopyN(buf, rd, 512) rd.Reset(bs) } } @@ -189,13 +190,13 @@ func BenchmarkCopyNLarge(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - CopyN(buf, rd, 32*1024) + io.CopyN(buf, rd, 32*1024) rd.Reset(bs) } } type noReadFrom struct { - w Writer + w io.Writer } func (w *noReadFrom) Write(p []byte) (n int, err error) { @@ -214,32 +215,32 @@ func TestCopyNEOF(t *testing.T) { b := new(bytes.Buffer) - n, err := CopyN(&noReadFrom{b}, strings.NewReader("foo"), 3) + n, err := io.CopyN(&noReadFrom{b}, strings.NewReader("foo"), 3) if n != 3 || err != nil { t.Errorf("CopyN(noReadFrom, foo, 3) = %d, %v; want 3, nil", n, err) } - n, err = CopyN(&noReadFrom{b}, strings.NewReader("foo"), 4) - if n != 3 || err != EOF { + n, err = io.CopyN(&noReadFrom{b}, strings.NewReader("foo"), 4) + if n != 3 || err != io.EOF { t.Errorf("CopyN(noReadFrom, foo, 4) = %d, %v; want 3, EOF", n, err) } - n, err = CopyN(b, strings.NewReader("foo"), 3) // b has read from + n, err = io.CopyN(b, strings.NewReader("foo"), 3) // b has read from if n != 3 || err != nil { t.Errorf("CopyN(bytes.Buffer, foo, 3) = %d, %v; want 3, nil", n, err) } - n, err = CopyN(b, strings.NewReader("foo"), 4) // b has read from - if n != 3 || err != EOF { + n, err = io.CopyN(b, strings.NewReader("foo"), 4) // b has read from + if n != 3 || err != io.EOF { t.Errorf("CopyN(bytes.Buffer, foo, 4) = %d, %v; want 3, EOF", n, err) } - n, err = CopyN(b, wantedAndErrReader{}, 5) + n, err = io.CopyN(b, wantedAndErrReader{}, 5) if n != 5 || err != nil { t.Errorf("CopyN(bytes.Buffer, wantedAndErrReader, 5) = %d, %v; want 5, nil", n, err) } - n, err = CopyN(&noReadFrom{b}, wantedAndErrReader{}, 5) + n, err = io.CopyN(&noReadFrom{b}, wantedAndErrReader{}, 5) if n != 5 || err != nil { t.Errorf("CopyN(noReadFrom, wantedAndErrReader, 5) = %d, %v; want 5, nil", n, err) } @@ -267,7 +268,7 @@ func (r *dataAndErrorBuffer) Read(p []byte) (n int, err error) { func TestReadAtLeastWithDataAndEOF(t *testing.T) { var rb dataAndErrorBuffer - rb.err = EOF + rb.err = io.EOF testReadAtLeast(t, &rb) } @@ -277,41 +278,41 @@ func TestReadAtLeastWithDataAndError(t *testing.T) { testReadAtLeast(t, &rb) } -func testReadAtLeast(t *testing.T, rb ReadWriter) { +func testReadAtLeast(t *testing.T, rb io.ReadWriter) { rb.Write([]byte("0123")) buf := make([]byte, 2) - n, err := ReadAtLeast(rb, buf, 2) + n, err := io.ReadAtLeast(rb, buf, 2) if err != nil { t.Error(err) } if n != 2 { t.Errorf("expected to have read 2 bytes, got %v", n) } - n, err = ReadAtLeast(rb, buf, 4) - if err != ErrShortBuffer { + n, err = io.ReadAtLeast(rb, buf, 4) + if err != io.ErrShortBuffer { t.Errorf("expected `ErrShortBuffer` got %v", err) } if n != 0 { t.Errorf("expected to have read 0 bytes, got %v", n) } - n, err = ReadAtLeast(rb, buf, 1) + n, err = io.ReadAtLeast(rb, buf, 1) if err != nil { t.Error(err) } if n != 2 { t.Errorf("expected to have read 2 bytes, got %v", n) } - n, err = ReadAtLeast(rb, buf, 2) - if err != EOF { + n, err = io.ReadAtLeast(rb, buf, 2) + if err != io.EOF { t.Errorf("expected EOF, got %v", err) } if n != 0 { t.Errorf("expected to have read 0 bytes, got %v", n) } rb.Write([]byte("4")) - n, err = ReadAtLeast(rb, buf, 2) - want := ErrUnexpectedEOF - if rb, ok := rb.(*dataAndErrorBuffer); ok && rb.err != EOF { + n, err = io.ReadAtLeast(rb, buf, 2) + want := io.ErrUnexpectedEOF + if rb, ok := rb.(*dataAndErrorBuffer); ok && rb.err != io.EOF { want = rb.err } if err != want { @@ -338,7 +339,7 @@ func TestTeeReader(t *testing.T) { if !bytes.Equal(wb.Bytes(), src) { t.Errorf("bytes written = %q want %q", wb.Bytes(), src) } - if n, err := r.Read(dst); n != 0 || err != EOF { + if n, err := r.Read(dst); n != 0 || err != io.EOF { t.Errorf("r.Read at EOF = %d, %v want 0, EOF", n, err) } rb = bytes.NewBuffer(src) @@ -362,22 +363,22 @@ func TestSectionReader_ReadAt(t *testing.T) { exp string err error }{ - {data: "", off: 0, n: 10, bufLen: 2, at: 0, exp: "", err: EOF}, + {data: "", off: 0, n: 10, bufLen: 2, at: 0, exp: "", err: io.EOF}, {data: dat, off: 0, n: len(dat), bufLen: 0, at: 0, exp: "", err: nil}, - {data: dat, off: len(dat), n: 1, bufLen: 1, at: 0, exp: "", err: EOF}, + {data: dat, off: len(dat), n: 1, bufLen: 1, at: 0, exp: "", err: io.EOF}, {data: dat, off: 0, n: len(dat) + 2, bufLen: len(dat), at: 0, exp: dat, err: nil}, {data: dat, off: 0, n: len(dat), bufLen: len(dat) / 2, at: 0, exp: dat[:len(dat)/2], err: nil}, {data: dat, off: 0, n: len(dat), bufLen: len(dat), at: 0, exp: dat, err: nil}, {data: dat, off: 0, n: len(dat), bufLen: len(dat) / 2, at: 2, exp: dat[2 : 2+len(dat)/2], err: nil}, {data: dat, off: 3, n: len(dat), bufLen: len(dat) / 2, at: 2, exp: dat[5 : 5+len(dat)/2], err: nil}, {data: dat, off: 3, n: len(dat) / 2, bufLen: len(dat)/2 - 2, at: 2, exp: dat[5 : 5+len(dat)/2-2], err: nil}, - {data: dat, off: 3, n: len(dat) / 2, bufLen: len(dat)/2 + 2, at: 2, exp: dat[5 : 5+len(dat)/2-2], err: EOF}, - {data: dat, off: 0, n: 0, bufLen: 0, at: -1, exp: "", err: EOF}, - {data: dat, off: 0, n: 0, bufLen: 0, at: 1, exp: "", err: EOF}, + {data: dat, off: 3, n: len(dat) / 2, bufLen: len(dat)/2 + 2, at: 2, exp: dat[5 : 5+len(dat)/2-2], err: io.EOF}, + {data: dat, off: 0, n: 0, bufLen: 0, at: -1, exp: "", err: io.EOF}, + {data: dat, off: 0, n: 0, bufLen: 0, at: 1, exp: "", err: io.EOF}, } for i, tt := range tests { r := strings.NewReader(tt.data) - s := NewSectionReader(r, int64(tt.off), int64(tt.n)) + s := io.NewSectionReader(r, int64(tt.off), int64(tt.n)) buf := make([]byte, tt.bufLen) if n, err := s.ReadAt(buf, int64(tt.at)); n != len(tt.exp) || string(buf[:n]) != tt.exp || err != tt.err { t.Fatalf("%d: ReadAt(%d) = %q, %v; expected %q, %v", i, tt.at, buf[:n], err, tt.exp, tt.err) @@ -388,9 +389,9 @@ func TestSectionReader_ReadAt(t *testing.T) { func TestSectionReader_Seek(t *testing.T) { // Verifies that NewSectionReader's Seeker behaves like bytes.NewReader (which is like strings.NewReader) br := bytes.NewReader([]byte("foo")) - sr := NewSectionReader(br, 0, int64(len("foo"))) + sr := io.NewSectionReader(br, 0, int64(len("foo"))) - for _, whence := range []int{SeekStart, SeekCurrent, SeekEnd} { + for _, whence := range []int{io.SeekStart, io.SeekCurrent, io.SeekEnd} { for offset := int64(-3); offset <= 4; offset++ { brOff, brErr := br.Seek(offset, whence) srOff, srErr := sr.Seek(offset, whence) @@ -402,13 +403,13 @@ func TestSectionReader_Seek(t *testing.T) { } // And verify we can just seek past the end and get an EOF - got, err := sr.Seek(100, SeekStart) + got, err := sr.Seek(100, io.SeekStart) if err != nil || got != 100 { t.Errorf("Seek = %v, %v; want 100, nil", got, err) } n, err := sr.Read(make([]byte, 10)) - if n != 0 || err != EOF { + if n != 0 || err != io.EOF { t.Errorf("Read = %v, %v; want 0, EOF", n, err) } } @@ -424,7 +425,7 @@ func TestSectionReader_Size(t *testing.T) { for _, tt := range tests { r := strings.NewReader(tt.data) - sr := NewSectionReader(r, 0, int64(len(tt.data))) + sr := io.NewSectionReader(r, 0, int64(len(tt.data))) if got := sr.Size(); got != tt.want { t.Errorf("Size = %v; want %v", got, tt.want) } @@ -442,11 +443,11 @@ func (w largeWriter) Write(p []byte) (int, error) { } func TestCopyLargeWriter(t *testing.T) { - want := errInvalidWrite + want := io.ErrInvalidWrite rb := new(Buffer) wb := largeWriter{} rb.WriteString("hello, world.") - if _, err := Copy(wb, rb); err != want { + if _, err := io.Copy(wb, rb); err != want { t.Errorf("Copy error: got %v, want %v", err, want) } @@ -454,7 +455,7 @@ func TestCopyLargeWriter(t *testing.T) { rb = new(Buffer) wb = largeWriter{err: want} rb.WriteString("hello, world.") - if _, err := Copy(wb, rb); err != want { + if _, err := io.Copy(wb, rb); err != want { t.Errorf("Copy error: got %v, want %v", err, want) } } @@ -462,18 +463,18 @@ func TestCopyLargeWriter(t *testing.T) { func TestNopCloserWriterToForwarding(t *testing.T) { for _, tc := range [...]struct { Name string - r Reader + r io.Reader }{ - {"not a WriterTo", Reader(nil)}, + {"not a WriterTo", io.Reader(nil)}, {"a WriterTo", struct { - Reader - WriterTo + io.Reader + io.WriterTo }{}}, } { - nc := NopCloser(tc.r) + nc := io.NopCloser(tc.r) - _, expected := tc.r.(WriterTo) - _, got := nc.(WriterTo) + _, expected := tc.r.(io.WriterTo) + _, got := nc.(io.WriterTo) if expected != got { t.Errorf("NopCloser incorrectly forwards WriterTo for %s, got %t want %t", tc.Name, got, expected) } @@ -488,28 +489,28 @@ func TestNopCloserWriterToForwarding(t *testing.T) { // t.Fatalf("CreateTemp(%s) failed: %v", tmpfilename, err) // } // defer tmpfile.Close() -// w := NewOffsetWriter(tmpfile, 0) +// w := io.NewOffsetWriter(tmpfile, 0) // // Should throw error errWhence if whence is not valid // t.Run("errWhence", func(t *testing.T) { // for _, whence := range []int{-3, -2, -1, 3, 4, 5} { // var offset int64 = 0 // gotOff, gotErr := w.Seek(offset, whence) -// if gotOff != 0 || gotErr != errWhence { +// if gotOff != 0 || gotErr != io.ErrWhence { // t.Errorf("For whence %d, offset %d, OffsetWriter.Seek got: (%d, %v), want: (%d, %v)", -// whence, offset, gotOff, gotErr, 0, errWhence) +// whence, offset, gotOff, gotErr, 0, io.ErrWhence) // } // } // }) // // Should throw error errOffset if offset is negative // t.Run("errOffset", func(t *testing.T) { -// for _, whence := range []int{SeekStart, SeekCurrent} { +// for _, whence := range []int{io.SeekStart, io.SeekCurrent} { // for offset := int64(-3); offset < 0; offset++ { // gotOff, gotErr := w.Seek(offset, whence) -// if gotOff != 0 || gotErr != errOffset { +// if gotOff != 0 || gotErr != io.ErrOffset { // t.Errorf("For whence %d, offset %d, OffsetWriter.Seek got: (%d, %v), want: (%d, %v)", -// whence, offset, gotOff, gotErr, 0, ErrOffset) +// whence, offset, gotOff, gotErr, 0, io.ErrOffset) // } // } // } @@ -523,12 +524,12 @@ func TestNopCloserWriterToForwarding(t *testing.T) { // returnOff int64 // }{ // // keep in order -// {whence: SeekStart, offset: 1, returnOff: 1}, -// {whence: SeekStart, offset: 2, returnOff: 2}, -// {whence: SeekStart, offset: 3, returnOff: 3}, -// {whence: SeekCurrent, offset: 1, returnOff: 4}, -// {whence: SeekCurrent, offset: 2, returnOff: 6}, -// {whence: SeekCurrent, offset: 3, returnOff: 9}, +// {whence: io.SeekStart, offset: 1, returnOff: 1}, +// {whence: io.SeekStart, offset: 2, returnOff: 2}, +// {whence: io.SeekStart, offset: 3, returnOff: 3}, +// {whence: io.SeekCurrent, offset: 1, returnOff: 4}, +// {whence: io.SeekCurrent, offset: 2, returnOff: 6}, +// {whence: io.SeekCurrent, offset: 3, returnOff: 9}, // } // for idx, tt := range tests { // gotOff, gotErr := w.Seek(tt.offset, tt.whence) @@ -546,28 +547,28 @@ func TestNopCloserWriterToForwarding(t *testing.T) { // to use the original approach instead of this method. (just un-comment the test above) func TestOffsetWriter_Seek(t *testing.T) { buf := new(bytes.Buffer) - w := NewOffsetWriter(testWriterAt{buf}, 0) + w := io.NewOffsetWriter(testWriterAt{buf}, 0) // Should throw error errWhence if whence is not valid t.Run("errWhence", func(t *testing.T) { for _, whence := range []int{-3, -2, -1, 3, 4, 5} { var offset int64 = 0 gotOff, gotErr := w.Seek(offset, whence) - if gotOff != 0 || gotErr != errWhence { + if gotOff != 0 || gotErr != io.ErrWhence { t.Errorf("For whence %d, offset %d, OffsetWriter.Seek got: (%d, %v), want: (%d, %v)", - whence, offset, gotOff, gotErr, 0, errWhence) + whence, offset, gotOff, gotErr, 0, io.ErrWhence) } } }) // Should throw error errOffset if offset is negative t.Run("errOffset", func(t *testing.T) { - for _, whence := range []int{SeekStart, SeekCurrent} { + for _, whence := range []int{io.SeekStart, io.SeekCurrent} { for offset := int64(-3); offset < 0; offset++ { gotOff, gotErr := w.Seek(offset, whence) - if gotOff != 0 || gotErr != errOffset { + if gotOff != 0 || gotErr != io.ErrOffset { t.Errorf("For whence %d, offset %d, OffsetWriter.Seek got: (%d, %v), want: (%d, %v)", - whence, offset, gotOff, gotErr, 0, errOffset) + whence, offset, gotOff, gotErr, 0, io.ErrOffset) } } } @@ -579,12 +580,12 @@ func TestOffsetWriter_Seek(t *testing.T) { whence int returnOff int64 }{ - {whence: SeekStart, offset: 1, returnOff: 1}, - {whence: SeekStart, offset: 2, returnOff: 2}, - {whence: SeekStart, offset: 3, returnOff: 3}, - {whence: SeekCurrent, offset: 1, returnOff: 4}, - {whence: SeekCurrent, offset: 2, returnOff: 6}, - {whence: SeekCurrent, offset: 3, returnOff: 9}, + {whence: io.SeekStart, offset: 1, returnOff: 1}, + {whence: io.SeekStart, offset: 2, returnOff: 2}, + {whence: io.SeekStart, offset: 3, returnOff: 3}, + {whence: io.SeekCurrent, offset: 1, returnOff: 4}, + {whence: io.SeekCurrent, offset: 2, returnOff: 6}, + {whence: io.SeekCurrent, offset: 3, returnOff: 9}, } for idx, tt := range tests { gotOff, gotErr := w.Seek(tt.offset, tt.whence) diff --git a/gnovm/stdlibs/io/multi_test.gno b/gnovm/stdlibs/io/multi_test.gno index f39895ea776..908dffd3cce 100644 --- a/gnovm/stdlibs/io/multi_test.gno +++ b/gnovm/stdlibs/io/multi_test.gno @@ -1,4 +1,4 @@ -package io +package io_test // Copyright 2010 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -8,6 +8,7 @@ import ( "bytes" "crypto/sha256" "fmt" + "io" "strings" "testing" ) @@ -17,14 +18,14 @@ type Stringer interface { } func TestMultiReader(t *testing.T) { - var mr Reader + var mr io.Reader var buf []byte nread := 0 withFooBar := func(tests func()) { r1 := strings.NewReader("foo ") r2 := strings.NewReader("") r3 := strings.NewReader("bar") - mr = MultiReader(r1, r2, r3) + mr = io.MultiReader(r1, r2, r3) buf = make([]byte, 20) tests() } @@ -50,13 +51,13 @@ func TestMultiReader(t *testing.T) { expectRead(2, "fo", nil) expectRead(5, "o ", nil) expectRead(5, "bar", nil) - expectRead(5, "", EOF) + expectRead(5, "", io.EOF) }) withFooBar(func() { expectRead(4, "foo ", nil) expectRead(1, "b", nil) expectRead(3, "ar", nil) - expectRead(1, "", EOF) + expectRead(1, "", io.EOF) }) withFooBar(func() { expectRead(5, "foo ", nil) @@ -67,7 +68,7 @@ func TestMultiWriter(t *testing.T) { sink := new(bytes.Buffer) // Hide bytes.Buffer's WriteString method: testMultiWriter(t, struct { - Writer + io.Writer Stringer }{sink, sink}) } @@ -82,11 +83,11 @@ func TestMultiWriter_String(t *testing.T) { func TestMultiWriter_WriteStringSingleAlloc(t *testing.T) { var sink1, sink2 bytes.Buffer type simpleWriter struct { // hide bytes.Buffer's WriteString - Writer + io.Writer } - mw := MultiWriter(simpleWriter{&sink1}, simpleWriter{&sink2}) + mw := io.MultiWriter(simpleWriter{&sink1}, simpleWriter{&sink2}) allocs := int(testing.AllocsPerRun2(1000, func() { - WriteString(mw, "foo") + io.WriteString(mw, "foo") })) if allocs != 1 { t.Errorf("num allocations = %d; want 1", allocs) @@ -107,24 +108,24 @@ func (c *writeStringChecker) Write(p []byte) (n int, err error) { func TestMultiWriter_StringCheckCall(t *testing.T) { var c writeStringChecker - mw := MultiWriter(&c) - WriteString(mw, "foo") + mw := io.MultiWriter(&c) + io.WriteString(mw, "foo") if !c.called { t.Error("did not see WriteString call to writeStringChecker") } } func testMultiWriter(t *testing.T, sink interface { - Writer + io.Writer Stringer }, ) { var buf bytes.Buffer - mw := MultiWriter(&buf, sink) + mw := io.MultiWriter(&buf, sink) sourceString := "My input text." source := strings.NewReader(sourceString) - written, err := Copy(mw, source) + written, err := io.Copy(mw, source) if written != int64(len(sourceString)) { t.Errorf("short write of %d, not %d", written, len(sourceString)) @@ -158,7 +159,7 @@ func TestMultiWriterSingleChainFlatten(t *testing.T) { n := runtime.Callers(0, pc) var myDepth = callDepth(pc[:n]) var writeDepth int // will contain the depth from which writerFunc.Writer was called - var w Writer = MultiWriter(writerFunc(func(p []byte) (int, error) { + var w io.Writer = io.MultiWriter(writerFunc(func(p []byte) (int, error) { n := runtime.Callers(1, pc) writeDepth += callDepth(pc[:n]) return 0, nil @@ -167,10 +168,10 @@ func TestMultiWriterSingleChainFlatten(t *testing.T) { mw := w // chain a bunch of multiWriters for i := 0; i < 100; i++ { - mw = MultiWriter(w) + mw = io.MultiWriter(w) } - mw = MultiWriter(w, mw, w, mw) + mw = io.MultiWriter(w, mw, w, mw) mw.Write(nil) // don't care about errors, just want to check the call-depth for Write if writeDepth != 4*(myDepth+2) { // 2 should be multiWriter.Write and writerFunc.Write @@ -182,25 +183,25 @@ func TestMultiWriterSingleChainFlatten(t *testing.T) { func TestMultiWriterError(t *testing.T) { f1 := writerFunc(func(p []byte) (int, error) { - return len(p) / 2, ErrShortWrite + return len(p) / 2, io.ErrShortWrite }) f2 := writerFunc(func(p []byte) (int, error) { t.Errorf("MultiWriter called f2.Write") return len(p), nil }) - w := MultiWriter(f1, f2) + w := io.MultiWriter(f1, f2) n, err := w.Write(make([]byte, 100)) - if n != 50 || err != ErrShortWrite { + if n != 50 || err != io.ErrShortWrite { t.Errorf("Write = %d, %v, want 50, ErrShortWrite", n, err) } } // Test that MultiReader copies the input slice and is insulated from future modification. func TestMultiReaderCopy(t *testing.T) { - slice := []Reader{strings.NewReader("hello world")} - r := MultiReader(slice...) + slice := []io.Reader{strings.NewReader("hello world")} + r := io.MultiReader(slice...) slice[0] = nil - data, err := ReadAll(r) + data, err := io.ReadAll(r) if err != nil || string(data) != "hello world" { t.Errorf("ReadAll() = %q, %v, want %q, nil", data, err, "hello world") } @@ -209,8 +210,8 @@ func TestMultiReaderCopy(t *testing.T) { // Test that MultiWriter copies the input slice and is insulated from future modification. func TestMultiWriterCopy(t *testing.T) { var buf bytes.Buffer - slice := []Writer{&buf} - w := MultiWriter(slice...) + slice := []io.Writer{&buf} + w := io.MultiWriter(slice...) slice[0] = nil n, err := w.Write([]byte("hello world")) if err != nil || n != 11 { @@ -246,7 +247,7 @@ func TestMultiReaderFlatten(t *testing.T) { n := runtime.Callers(0, pc) var myDepth = callDepth(pc[:n]) var readDepth int // will contain the depth from which fakeReader.Read was called - var r Reader = MultiReader(readerFunc(func(p []byte) (int, error) { + var r io.Reader = io.MultiReader(readerFunc(func(p []byte) (int, error) { n := runtime.Callers(1, pc) readDepth = callDepth(pc[:n]) return 0, errors.New("irrelevant") @@ -254,7 +255,7 @@ func TestMultiReaderFlatten(t *testing.T) { // chain a bunch of multiReaders for i := 0; i < 100; i++ { - r = MultiReader(r) + r = io.MultiReader(r) } r.Read(nil) // don't care about errors, just want to check the call-depth for Read @@ -277,12 +278,12 @@ func (b byteAndEOFReader) Read(p []byte) (n int, err error) { panic("unexpected call") } p[0] = byte(b) - return 1, EOF + return 1, io.EOF } // This used to yield bytes forever; issue 16795. func TestMultiReaderSingleByteWithEOF(t *testing.T) { - got, err := ReadAll(LimitReader(MultiReader(byteAndEOFReader('a'), byteAndEOFReader('b')), 10)) + got, err := io.ReadAll(io.LimitReader(io.MultiReader(byteAndEOFReader('a'), byteAndEOFReader('b')), 10)) if err != nil { t.Fatal(err) } @@ -296,17 +297,17 @@ func TestMultiReaderSingleByteWithEOF(t *testing.T) { // chain continues to return EOF on its final read, rather than // yielding a (0, EOF). func TestMultiReaderFinalEOF(t *testing.T) { - r := MultiReader(bytes.NewReader(nil), byteAndEOFReader('a')) + r := io.MultiReader(bytes.NewReader(nil), byteAndEOFReader('a')) buf := make([]byte, 2) n, err := r.Read(buf) - if n != 1 || err != EOF { + if n != 1 || err != io.EOF { t.Errorf("got %v, %v; want 1, EOF", n, err) } } /* func TestMultiReaderFreesExhaustedReaders(t *testing.T) { - var mr Reader + var mr io.Reader closed := make(chan struct{}) // The closure ensures that we don't have a live reference to buf1 // on our stack after MultiReader is inlined (Issue 18819). This @@ -314,14 +315,14 @@ func TestMultiReaderFreesExhaustedReaders(t *testing.T) { func() { buf1 := bytes.NewReader([]byte("foo")) buf2 := bytes.NewReader([]byte("bar")) - mr = MultiReader(buf1, buf2) - runtime.SetFinalizer(buf1, func(*bytes.Reader) { + mr = io.MultiReader(buf1, buf2) + runtime.SetFinalizer(buf1, func(*bytes.io.Reader) { close(closed) }) }() buf := make([]byte, 4) - if n, err := ReadFull(mr, buf); err != nil || string(buf) != "foob" { + if n, err := io.ReadFull(mr, buf); err != nil || string(buf) != "foob" { t.Fatalf(`ReadFull = %d (%q), %v; want 3, "foo", nil`, n, buf[:n], err) } @@ -332,7 +333,7 @@ func TestMultiReaderFreesExhaustedReaders(t *testing.T) { t.Fatal("timeout waiting for collection of buf1") } - if n, err := ReadFull(mr, buf[:2]); err != nil || string(buf[:2]) != "ar" { + if n, err := io.ReadFull(mr, buf[:2]); err != nil || string(buf[:2]) != "ar" { t.Fatalf(`ReadFull = %d (%q), %v; want 2, "ar", nil`, n, buf[:n], err) } } @@ -342,21 +343,21 @@ func TestInterleavedMultiReader(t *testing.T) { r1 := strings.NewReader("123") r2 := strings.NewReader("45678") - mr1 := MultiReader(r1, r2) - mr2 := MultiReader(mr1) + mr1 := io.MultiReader(r1, r2) + mr2 := io.MultiReader(mr1) buf := make([]byte, 4) // Have mr2 use mr1's []Readers. // Consume r1 (and clear it for GC to handle) and consume part of r2. - n, err := ReadFull(mr2, buf) + n, err := io.ReadFull(mr2, buf) if got := string(buf[:n]); got != "1234" || err != nil { t.Errorf(`ReadFull(mr2) = (%q, %v), want ("1234", nil)`, got, err) } // Consume the rest of r2 via mr1. // This should not panic even though mr2 cleared r1. - n, err = ReadFull(mr1, buf) + n, err = io.ReadFull(mr1, buf) if got := string(buf[:n]); got != "5678" || err != nil { t.Errorf(`ReadFull(mr1) = (%q, %v), want ("5678", nil)`, got, err) } diff --git a/gnovm/stdlibs/math/bits/bits_test.gno b/gnovm/stdlibs/math/bits/bits_test.gno index e2834cacf7c..b92de1b94d2 100644 --- a/gnovm/stdlibs/math/bits/bits_test.gno +++ b/gnovm/stdlibs/math/bits/bits_test.gno @@ -2,9 +2,10 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package bits +package bits_test import ( + "math/bits" "testing" ) @@ -14,7 +15,7 @@ func TestLeadingZeros(t *testing.T) { for k := 0; k < 64-8; k++ { x := uint64(i) << uint(k) if x <= 1<<8-1 { - got := LeadingZeros8(uint8(x)) + got := bits.LeadingZeros8(uint8(x)) want := nlz - k + (8 - 8) if x == 0 { want = 8 @@ -25,7 +26,7 @@ func TestLeadingZeros(t *testing.T) { } if x <= 1<<16-1 { - got := LeadingZeros16(uint16(x)) + got := bits.LeadingZeros16(uint16(x)) want := nlz - k + (16 - 8) if x == 0 { want = 16 @@ -36,7 +37,7 @@ func TestLeadingZeros(t *testing.T) { } if x <= 1<<32-1 { - got := LeadingZeros32(uint32(x)) + got := bits.LeadingZeros32(uint32(x)) want := nlz - k + (32 - 8) if x == 0 { want = 32 @@ -44,8 +45,8 @@ func TestLeadingZeros(t *testing.T) { if got != want { t.Fatalf("LeadingZeros32(%#08x) == %d; want %d", x, got, want) } - if UintSize == 32 { - got = LeadingZeros(uint(x)) + if bits.UintSize == 32 { + got = bits.LeadingZeros(uint(x)) if got != want { t.Fatalf("LeadingZeros(%#08x) == %d; want %d", x, got, want) } @@ -53,7 +54,7 @@ func TestLeadingZeros(t *testing.T) { } if x <= 1<<64-1 { - got := LeadingZeros64(uint64(x)) + got := bits.LeadingZeros64(uint64(x)) want := nlz - k + (64 - 8) if x == 0 { want = 64 @@ -61,8 +62,8 @@ func TestLeadingZeros(t *testing.T) { if got != want { t.Fatalf("LeadingZeros64(%#016x) == %d; want %d", x, got, want) } - if UintSize == 64 { - got = LeadingZeros(uint(x)) + if bits.UintSize == 64 { + got = bits.LeadingZeros(uint(x)) if got != want { t.Fatalf("LeadingZeros(%#016x) == %d; want %d", x, got, want) } @@ -75,7 +76,7 @@ func TestLeadingZeros(t *testing.T) { // Exported (global) variable serving as input for some // of the benchmarks to ensure side-effect free calls // are not optimized away. -var Input uint64 = DeBruijn64 +var Input uint64 = bits.DeBruijn64 // Exported (global) variable to store function results // during benchmarking to ensure side-effect free calls @@ -85,7 +86,7 @@ var Output int func BenchmarkLeadingZeros(b *testing.B) { var s int for i := 0; i < b.N; i++ { - s += LeadingZeros(uint(Input) >> (uint(i) % UintSize)) + s += bits.LeadingZeros(uint(Input) >> (uint(i) % bits.UintSize)) } Output = s } @@ -93,7 +94,7 @@ func BenchmarkLeadingZeros(b *testing.B) { func BenchmarkLeadingZeros8(b *testing.B) { var s int for i := 0; i < b.N; i++ { - s += LeadingZeros8(uint8(Input) >> (uint(i) % 8)) + s += bits.LeadingZeros8(uint8(Input) >> (uint(i) % 8)) } Output = s } @@ -101,7 +102,7 @@ func BenchmarkLeadingZeros8(b *testing.B) { func BenchmarkLeadingZeros16(b *testing.B) { var s int for i := 0; i < b.N; i++ { - s += LeadingZeros16(uint16(Input) >> (uint(i) % 16)) + s += bits.LeadingZeros16(uint16(Input) >> (uint(i) % 16)) } Output = s } @@ -109,7 +110,7 @@ func BenchmarkLeadingZeros16(b *testing.B) { func BenchmarkLeadingZeros32(b *testing.B) { var s int for i := 0; i < b.N; i++ { - s += LeadingZeros32(uint32(Input) >> (uint(i) % 32)) + s += bits.LeadingZeros32(uint32(Input) >> (uint(i) % 32)) } Output = s } @@ -117,7 +118,7 @@ func BenchmarkLeadingZeros32(b *testing.B) { func BenchmarkLeadingZeros64(b *testing.B) { var s int for i := 0; i < b.N; i++ { - s += LeadingZeros64(uint64(Input) >> (uint(i) % 64)) + s += bits.LeadingZeros64(uint64(Input) >> (uint(i) % 64)) } Output = s } @@ -129,7 +130,7 @@ func TestTrailingZeros(t *testing.T) { x := uint64(i) << uint(k) want := ntz + k if x <= 1<<8-1 { - got := TrailingZeros8(uint8(x)) + got := bits.TrailingZeros8(uint8(x)) if x == 0 { want = 8 } @@ -139,7 +140,7 @@ func TestTrailingZeros(t *testing.T) { } if x <= 1<<16-1 { - got := TrailingZeros16(uint16(x)) + got := bits.TrailingZeros16(uint16(x)) if x == 0 { want = 16 } @@ -149,15 +150,15 @@ func TestTrailingZeros(t *testing.T) { } if x <= 1<<32-1 { - got := TrailingZeros32(uint32(x)) + got := bits.TrailingZeros32(uint32(x)) if x == 0 { want = 32 } if got != want { t.Fatalf("TrailingZeros32(%#08x) == %d; want %d", x, got, want) } - if UintSize == 32 { - got = TrailingZeros(uint(x)) + if bits.UintSize == 32 { + got = bits.TrailingZeros(uint(x)) if got != want { t.Fatalf("TrailingZeros(%#08x) == %d; want %d", x, got, want) } @@ -165,15 +166,15 @@ func TestTrailingZeros(t *testing.T) { } if x <= 1<<64-1 { - got := TrailingZeros64(uint64(x)) + got := bits.TrailingZeros64(uint64(x)) if x == 0 { want = 64 } if got != want { t.Fatalf("TrailingZeros64(%#016x) == %d; want %d", x, got, want) } - if UintSize == 64 { - got = TrailingZeros(uint(x)) + if bits.UintSize == 64 { + got = bits.TrailingZeros(uint(x)) if got != want { t.Fatalf("TrailingZeros(%#016x) == %d; want %d", x, got, want) } @@ -186,7 +187,7 @@ func TestTrailingZeros(t *testing.T) { func BenchmarkTrailingZeros(b *testing.B) { var s int for i := 0; i < b.N; i++ { - s += TrailingZeros(uint(Input) << (uint(i) % UintSize)) + s += bits.TrailingZeros(uint(Input) << (uint(i) % bits.UintSize)) } Output = s } @@ -194,7 +195,7 @@ func BenchmarkTrailingZeros(b *testing.B) { func BenchmarkTrailingZeros8(b *testing.B) { var s int for i := 0; i < b.N; i++ { - s += TrailingZeros8(uint8(Input) << (uint(i) % 8)) + s += bits.TrailingZeros8(uint8(Input) << (uint(i) % 8)) } Output = s } @@ -202,7 +203,7 @@ func BenchmarkTrailingZeros8(b *testing.B) { func BenchmarkTrailingZeros16(b *testing.B) { var s int for i := 0; i < b.N; i++ { - s += TrailingZeros16(uint16(Input) << (uint(i) % 16)) + s += bits.TrailingZeros16(uint16(Input) << (uint(i) % 16)) } Output = s } @@ -210,7 +211,7 @@ func BenchmarkTrailingZeros16(b *testing.B) { func BenchmarkTrailingZeros32(b *testing.B) { var s int for i := 0; i < b.N; i++ { - s += TrailingZeros32(uint32(Input) << (uint(i) % 32)) + s += bits.TrailingZeros32(uint32(Input) << (uint(i) % 32)) } Output = s } @@ -218,7 +219,7 @@ func BenchmarkTrailingZeros32(b *testing.B) { func BenchmarkTrailingZeros64(b *testing.B) { var s int for i := 0; i < b.N; i++ { - s += TrailingZeros64(uint64(Input) << (uint(i) % 64)) + s += bits.TrailingZeros64(uint64(Input) << (uint(i) % 64)) } Output = s } @@ -244,26 +245,26 @@ func TestOnesCount(t *testing.T) { func testOnesCount(t *testing.T, x uint64, want int) { if x <= 1<<8-1 { - got := OnesCount8(uint8(x)) + got := bits.OnesCount8(uint8(x)) if got != want { t.Fatalf("OnesCount8(%#02x) == %d; want %d", uint8(x), got, want) } } if x <= 1<<16-1 { - got := OnesCount16(uint16(x)) + got := bits.OnesCount16(uint16(x)) if got != want { t.Fatalf("OnesCount16(%#04x) == %d; want %d", uint16(x), got, want) } } if x <= 1<<32-1 { - got := OnesCount32(uint32(x)) + got := bits.OnesCount32(uint32(x)) if got != want { t.Fatalf("OnesCount32(%#08x) == %d; want %d", uint32(x), got, want) } - if UintSize == 32 { - got = OnesCount(uint(x)) + if bits.UintSize == 32 { + got = bits.OnesCount(uint(x)) if got != want { t.Fatalf("OnesCount(%#08x) == %d; want %d", uint32(x), got, want) } @@ -271,12 +272,12 @@ func testOnesCount(t *testing.T, x uint64, want int) { } if x <= 1<<64-1 { - got := OnesCount64(uint64(x)) + got := bits.OnesCount64(uint64(x)) if got != want { t.Fatalf("OnesCount64(%#016x) == %d; want %d", x, got, want) } - if UintSize == 64 { - got = OnesCount(uint(x)) + if bits.UintSize == 64 { + got = bits.OnesCount(uint(x)) if got != want { t.Fatalf("OnesCount(%#016x) == %d; want %d", x, got, want) } @@ -287,7 +288,7 @@ func testOnesCount(t *testing.T, x uint64, want int) { func BenchmarkOnesCount(b *testing.B) { var s int for i := 0; i < b.N; i++ { - s += OnesCount(uint(Input)) + s += bits.OnesCount(uint(Input)) } Output = s } @@ -295,7 +296,7 @@ func BenchmarkOnesCount(b *testing.B) { func BenchmarkOnesCount8(b *testing.B) { var s int for i := 0; i < b.N; i++ { - s += OnesCount8(uint8(Input)) + s += bits.OnesCount8(uint8(Input)) } Output = s } @@ -303,7 +304,7 @@ func BenchmarkOnesCount8(b *testing.B) { func BenchmarkOnesCount16(b *testing.B) { var s int for i := 0; i < b.N; i++ { - s += OnesCount16(uint16(Input)) + s += bits.OnesCount16(uint16(Input)) } Output = s } @@ -311,7 +312,7 @@ func BenchmarkOnesCount16(b *testing.B) { func BenchmarkOnesCount32(b *testing.B) { var s int for i := 0; i < b.N; i++ { - s += OnesCount32(uint32(Input)) + s += bits.OnesCount32(uint32(Input)) } Output = s } @@ -319,78 +320,78 @@ func BenchmarkOnesCount32(b *testing.B) { func BenchmarkOnesCount64(b *testing.B) { var s int for i := 0; i < b.N; i++ { - s += OnesCount64(uint64(Input)) + s += bits.OnesCount64(uint64(Input)) } Output = s } func TestRotateLeft(t *testing.T) { - var m uint64 = DeBruijn64 + var m uint64 = bits.DeBruijn64 for k := uint(0); k < 128; k++ { x8 := uint8(m) - got8 := RotateLeft8(x8, int(k)) + got8 := bits.RotateLeft8(x8, int(k)) want8 := x8<<(k&0x7) | x8>>(8-k&0x7) if got8 != want8 { t.Fatalf("RotateLeft8(%#02x, %d) == %#02x; want %#02x", x8, k, got8, want8) } - got8 = RotateLeft8(want8, -int(k)) + got8 = bits.RotateLeft8(want8, -int(k)) if got8 != x8 { t.Fatalf("RotateLeft8(%#02x, -%d) == %#02x; want %#02x", want8, k, got8, x8) } x16 := uint16(m) - got16 := RotateLeft16(x16, int(k)) + got16 := bits.RotateLeft16(x16, int(k)) want16 := x16<<(k&0xf) | x16>>(16-k&0xf) if got16 != want16 { t.Fatalf("RotateLeft16(%#04x, %d) == %#04x; want %#04x", x16, k, got16, want16) } - got16 = RotateLeft16(want16, -int(k)) + got16 = bits.RotateLeft16(want16, -int(k)) if got16 != x16 { t.Fatalf("RotateLeft16(%#04x, -%d) == %#04x; want %#04x", want16, k, got16, x16) } x32 := uint32(m) - got32 := RotateLeft32(x32, int(k)) + got32 := bits.RotateLeft32(x32, int(k)) want32 := x32<<(k&0x1f) | x32>>(32-k&0x1f) if got32 != want32 { t.Fatalf("RotateLeft32(%#08x, %d) == %#08x; want %#08x", x32, k, got32, want32) } - got32 = RotateLeft32(want32, -int(k)) + got32 = bits.RotateLeft32(want32, -int(k)) if got32 != x32 { t.Fatalf("RotateLeft32(%#08x, -%d) == %#08x; want %#08x", want32, k, got32, x32) } - if UintSize == 32 { + if bits.UintSize == 32 { x := uint(m) - got := RotateLeft(x, int(k)) + got := bits.RotateLeft(x, int(k)) want := x<<(k&0x1f) | x>>(32-k&0x1f) if got != want { t.Fatalf("RotateLeft(%#08x, %d) == %#08x; want %#08x", x, k, got, want) } - got = RotateLeft(want, -int(k)) + got = bits.RotateLeft(want, -int(k)) if got != x { t.Fatalf("RotateLeft(%#08x, -%d) == %#08x; want %#08x", want, k, got, x) } } x64 := uint64(m) - got64 := RotateLeft64(x64, int(k)) + got64 := bits.RotateLeft64(x64, int(k)) want64 := x64<<(k&0x3f) | x64>>(64-k&0x3f) if got64 != want64 { t.Fatalf("RotateLeft64(%#016x, %d) == %#016x; want %#016x", x64, k, got64, want64) } - got64 = RotateLeft64(want64, -int(k)) + got64 = bits.RotateLeft64(want64, -int(k)) if got64 != x64 { t.Fatalf("RotateLeft64(%#016x, -%d) == %#016x; want %#016x", want64, k, got64, x64) } - if UintSize == 64 { + if bits.UintSize == 64 { x := uint(m) - got := RotateLeft(x, int(k)) + got := bits.RotateLeft(x, int(k)) want := x<<(k&0x3f) | x>>(64-k&0x3f) if got != want { t.Fatalf("RotateLeft(%#016x, %d) == %#016x; want %#016x", x, k, got, want) } - got = RotateLeft(want, -int(k)) + got = bits.RotateLeft(want, -int(k)) if got != x { t.Fatalf("RotateLeft(%#08x, -%d) == %#08x; want %#08x", want, k, got, x) } @@ -401,7 +402,7 @@ func TestRotateLeft(t *testing.T) { func BenchmarkRotateLeft(b *testing.B) { var s uint for i := 0; i < b.N; i++ { - s += RotateLeft(uint(Input), i) + s += bits.RotateLeft(uint(Input), i) } Output = int(s) } @@ -409,7 +410,7 @@ func BenchmarkRotateLeft(b *testing.B) { func BenchmarkRotateLeft8(b *testing.B) { var s uint8 for i := 0; i < b.N; i++ { - s += RotateLeft8(uint8(Input), i) + s += bits.RotateLeft8(uint8(Input), i) } Output = int(s) } @@ -417,7 +418,7 @@ func BenchmarkRotateLeft8(b *testing.B) { func BenchmarkRotateLeft16(b *testing.B) { var s uint16 for i := 0; i < b.N; i++ { - s += RotateLeft16(uint16(Input), i) + s += bits.RotateLeft16(uint16(Input), i) } Output = int(s) } @@ -425,7 +426,7 @@ func BenchmarkRotateLeft16(b *testing.B) { func BenchmarkRotateLeft32(b *testing.B) { var s uint32 for i := 0; i < b.N; i++ { - s += RotateLeft32(uint32(Input), i) + s += bits.RotateLeft32(uint32(Input), i) } Output = int(s) } @@ -433,7 +434,7 @@ func BenchmarkRotateLeft32(b *testing.B) { func BenchmarkRotateLeft64(b *testing.B) { var s uint64 for i := 0; i < b.N; i++ { - s += RotateLeft64(uint64(Input), i) + s += bits.RotateLeft64(uint64(Input), i) } Output = int(s) } @@ -474,41 +475,41 @@ func TestReverse(t *testing.T) { func testReverse(t *testing.T, x64, want64 uint64) { x8 := uint8(x64) - got8 := Reverse8(x8) + got8 := bits.Reverse8(x8) want8 := uint8(want64 >> (64 - 8)) if got8 != want8 { t.Fatalf("Reverse8(%#02x) == %#02x; want %#02x", x8, got8, want8) } x16 := uint16(x64) - got16 := Reverse16(x16) + got16 := bits.Reverse16(x16) want16 := uint16(want64 >> (64 - 16)) if got16 != want16 { t.Fatalf("Reverse16(%#04x) == %#04x; want %#04x", x16, got16, want16) } x32 := uint32(x64) - got32 := Reverse32(x32) + got32 := bits.Reverse32(x32) want32 := uint32(want64 >> (64 - 32)) if got32 != want32 { t.Fatalf("Reverse32(%#08x) == %#08x; want %#08x", x32, got32, want32) } - if UintSize == 32 { + if bits.UintSize == 32 { x := uint(x32) - got := Reverse(x) + got := bits.Reverse(x) want := uint(want32) if got != want { t.Fatalf("Reverse(%#08x) == %#08x; want %#08x", x, got, want) } } - got64 := Reverse64(x64) + got64 := bits.Reverse64(x64) if got64 != want64 { t.Fatalf("Reverse64(%#016x) == %#016x; want %#016x", x64, got64, want64) } - if UintSize == 64 { + if bits.UintSize == 64 { x := uint(x64) - got := Reverse(x) + got := bits.Reverse(x) want := uint(want64) if got != want { t.Fatalf("Reverse(%#08x) == %#016x; want %#016x", x, got, want) @@ -519,7 +520,7 @@ func testReverse(t *testing.T, x64, want64 uint64) { func BenchmarkReverse(b *testing.B) { var s uint for i := 0; i < b.N; i++ { - s += Reverse(uint(i)) + s += bits.Reverse(uint(i)) } Output = int(s) } @@ -527,7 +528,7 @@ func BenchmarkReverse(b *testing.B) { func BenchmarkReverse8(b *testing.B) { var s uint8 for i := 0; i < b.N; i++ { - s += Reverse8(uint8(i)) + s += bits.Reverse8(uint8(i)) } Output = int(s) } @@ -535,7 +536,7 @@ func BenchmarkReverse8(b *testing.B) { func BenchmarkReverse16(b *testing.B) { var s uint16 for i := 0; i < b.N; i++ { - s += Reverse16(uint16(i)) + s += bits.Reverse16(uint16(i)) } Output = int(s) } @@ -543,7 +544,7 @@ func BenchmarkReverse16(b *testing.B) { func BenchmarkReverse32(b *testing.B) { var s uint32 for i := 0; i < b.N; i++ { - s += Reverse32(uint32(i)) + s += bits.Reverse32(uint32(i)) } Output = int(s) } @@ -551,7 +552,7 @@ func BenchmarkReverse32(b *testing.B) { func BenchmarkReverse64(b *testing.B) { var s uint64 for i := 0; i < b.N; i++ { - s += Reverse64(uint64(i)) + s += bits.Reverse64(uint64(i)) } Output = int(s) } @@ -577,34 +578,34 @@ func TestReverseBytes(t *testing.T) { func testReverseBytes(t *testing.T, x64, want64 uint64) { x16 := uint16(x64) - got16 := ReverseBytes16(x16) + got16 := bits.ReverseBytes16(x16) want16 := uint16(want64 >> (64 - 16)) if got16 != want16 { t.Fatalf("ReverseBytes16(%#04x) == %#04x; want %#04x", x16, got16, want16) } x32 := uint32(x64) - got32 := ReverseBytes32(x32) + got32 := bits.ReverseBytes32(x32) want32 := uint32(want64 >> (64 - 32)) if got32 != want32 { t.Fatalf("ReverseBytes32(%#08x) == %#08x; want %#08x", x32, got32, want32) } - if UintSize == 32 { + if bits.UintSize == 32 { x := uint(x32) - got := ReverseBytes(x) + got := bits.ReverseBytes(x) want := uint(want32) if got != want { t.Fatalf("ReverseBytes(%#08x) == %#08x; want %#08x", x, got, want) } } - got64 := ReverseBytes64(x64) + got64 := bits.ReverseBytes64(x64) if got64 != want64 { t.Fatalf("ReverseBytes64(%#016x) == %#016x; want %#016x", x64, got64, want64) } - if UintSize == 64 { + if bits.UintSize == 64 { x := uint(x64) - got := ReverseBytes(x) + got := bits.ReverseBytes(x) want := uint(want64) if got != want { t.Fatalf("ReverseBytes(%#016x) == %#016x; want %#016x", x, got, want) @@ -615,7 +616,7 @@ func testReverseBytes(t *testing.T, x64, want64 uint64) { func BenchmarkReverseBytes(b *testing.B) { var s uint for i := 0; i < b.N; i++ { - s += ReverseBytes(uint(i)) + s += bits.ReverseBytes(uint(i)) } Output = int(s) } @@ -623,7 +624,7 @@ func BenchmarkReverseBytes(b *testing.B) { func BenchmarkReverseBytes16(b *testing.B) { var s uint16 for i := 0; i < b.N; i++ { - s += ReverseBytes16(uint16(i)) + s += bits.ReverseBytes16(uint16(i)) } Output = int(s) } @@ -631,7 +632,7 @@ func BenchmarkReverseBytes16(b *testing.B) { func BenchmarkReverseBytes32(b *testing.B) { var s uint32 for i := 0; i < b.N; i++ { - s += ReverseBytes32(uint32(i)) + s += bits.ReverseBytes32(uint32(i)) } Output = int(s) } @@ -639,7 +640,7 @@ func BenchmarkReverseBytes32(b *testing.B) { func BenchmarkReverseBytes64(b *testing.B) { var s uint64 for i := 0; i < b.N; i++ { - s += ReverseBytes64(uint64(i)) + s += bits.ReverseBytes64(uint64(i)) } Output = int(s) } @@ -654,26 +655,26 @@ func TestLen(t *testing.T) { want = length + k } if x <= 1<<8-1 { - got := Len8(uint8(x)) + got := bits.Len8(uint8(x)) if got != want { t.Fatalf("Len8(%#02x) == %d; want %d", x, got, want) } } if x <= 1<<16-1 { - got := Len16(uint16(x)) + got := bits.Len16(uint16(x)) if got != want { t.Fatalf("Len16(%#04x) == %d; want %d", x, got, want) } } if x <= 1<<32-1 { - got := Len32(uint32(x)) + got := bits.Len32(uint32(x)) if got != want { t.Fatalf("Len32(%#08x) == %d; want %d", x, got, want) } - if UintSize == 32 { - got := Len(uint(x)) + if bits.UintSize == 32 { + got := bits.Len(uint(x)) if got != want { t.Fatalf("Len(%#08x) == %d; want %d", x, got, want) } @@ -681,12 +682,12 @@ func TestLen(t *testing.T) { } if x <= 1<<64-1 { - got := Len64(uint64(x)) + got := bits.Len64(uint64(x)) if got != want { t.Fatalf("Len64(%#016x) == %d; want %d", x, got, want) } - if UintSize == 64 { - got := Len(uint(x)) + if bits.UintSize == 64 { + got := bits.Len(uint(x)) if got != want { t.Fatalf("Len(%#016x) == %d; want %d", x, got, want) } @@ -697,7 +698,7 @@ func TestLen(t *testing.T) { } const ( - _M = 1< 0 { panic("overflow") } return x }, func(a, b uint64) uint64 { - x, c := Add64(a, b, 0) + x, c := bits.Add64(a, b, 0) if c != 0 { panic("overflow") } return x }, func(a, b uint64) uint64 { - x, c := Add64(a, b, 0) + x, c := bits.Add64(a, b, 0) if c == 1 { panic("overflow") } return x }, func(a, b uint64) uint64 { - x, c := Add64(a, b, 0) + x, c := bits.Add64(a, b, 0) if c != 1 { return x } panic("overflow") }, func(a, b uint64) uint64 { - x, c := Add64(a, b, 0) + x, c := bits.Add64(a, b, 0) if c == 0 { return x } @@ -863,35 +864,35 @@ func TestSub64OverflowPanic(t *testing.T) { // These are designed to improve coverage of compiler intrinsics. tests := []func(uint64, uint64) uint64{ func(a, b uint64) uint64 { - x, c := Sub64(a, b, 0) + x, c := bits.Sub64(a, b, 0) if c > 0 { panic("overflow") } return x }, func(a, b uint64) uint64 { - x, c := Sub64(a, b, 0) + x, c := bits.Sub64(a, b, 0) if c != 0 { panic("overflow") } return x }, func(a, b uint64) uint64 { - x, c := Sub64(a, b, 0) + x, c := bits.Sub64(a, b, 0) if c == 1 { panic("overflow") } return x }, func(a, b uint64) uint64 { - x, c := Sub64(a, b, 0) + x, c := bits.Sub64(a, b, 0) if c != 1 { return x } panic("overflow") }, func(a, b uint64) uint64 { - x, c := Sub64(a, b, 0) + x, c := bits.Sub64(a, b, 0) if c == 0 { return x } @@ -937,19 +938,19 @@ func TestMulDiv(t *testing.T) { x, y uint hi, lo, r uint }{ - {1 << (UintSize - 1), 2, 1, 0, 1}, + {1 << (bits.UintSize - 1), 2, 1, 0, 1}, {_M, _M, _M - 1, 1, 42}, } { - testMul("Mul", Mul, a.x, a.y, a.hi, a.lo) - testMul("Mul symmetric", Mul, a.y, a.x, a.hi, a.lo) - testDiv("Div", Div, a.hi, a.lo+a.r, a.y, a.x, a.r) - testDiv("Div symmetric", Div, a.hi, a.lo+a.r, a.x, a.y, a.r) + testMul("Mul", bits.Mul, a.x, a.y, a.hi, a.lo) + testMul("Mul symmetric", bits.Mul, a.y, a.x, a.hi, a.lo) + testDiv("Div", bits.Div, a.hi, a.lo+a.r, a.y, a.x, a.r) + testDiv("Div symmetric", bits.Div, a.hi, a.lo+a.r, a.x, a.y, a.r) // The above code can't test intrinsic implementation, because the passed function is not called directly. // The following code uses a closure to test the intrinsic version in case the function is intrinsified. - testMul("Mul intrinsic", func(x, y uint) (uint, uint) { return Mul(x, y) }, a.x, a.y, a.hi, a.lo) - testMul("Mul intrinsic symmetric", func(x, y uint) (uint, uint) { return Mul(x, y) }, a.y, a.x, a.hi, a.lo) - testDiv("Div intrinsic", func(hi, lo, y uint) (uint, uint) { return Div(hi, lo, y) }, a.hi, a.lo+a.r, a.y, a.x, a.r) - testDiv("Div intrinsic symmetric", func(hi, lo, y uint) (uint, uint) { return Div(hi, lo, y) }, a.hi, a.lo+a.r, a.x, a.y, a.r) + testMul("Mul intrinsic", func(x, y uint) (uint, uint) { return bits.Mul(x, y) }, a.x, a.y, a.hi, a.lo) + testMul("Mul intrinsic symmetric", func(x, y uint) (uint, uint) { return bits.Mul(x, y) }, a.y, a.x, a.hi, a.lo) + testDiv("Div intrinsic", func(hi, lo, y uint) (uint, uint) { return bits.Div(hi, lo, y) }, a.hi, a.lo+a.r, a.y, a.x, a.r) + testDiv("Div intrinsic symmetric", func(hi, lo, y uint) (uint, uint) { return bits.Div(hi, lo, y) }, a.hi, a.lo+a.r, a.x, a.y, a.r) } } @@ -974,10 +975,10 @@ func TestMulDiv32(t *testing.T) { {0xc47dfa8c, 50911, 0x98a4, 0x998587f4, 13}, {_M32, _M32, _M32 - 1, 1, 42}, } { - testMul("Mul32", Mul32, a.x, a.y, a.hi, a.lo) - testMul("Mul32 symmetric", Mul32, a.y, a.x, a.hi, a.lo) - testDiv("Div32", Div32, a.hi, a.lo+a.r, a.y, a.x, a.r) - testDiv("Div32 symmetric", Div32, a.hi, a.lo+a.r, a.x, a.y, a.r) + testMul("Mul32", bits.Mul32, a.x, a.y, a.hi, a.lo) + testMul("Mul32 symmetric", bits.Mul32, a.y, a.x, a.hi, a.lo) + testDiv("Div32", bits.Div32, a.hi, a.lo+a.r, a.y, a.x, a.r) + testDiv("Div32 symmetric", bits.Div32, a.hi, a.lo+a.r, a.x, a.y, a.r) } } @@ -1002,16 +1003,16 @@ func TestMulDiv64(t *testing.T) { {0x3626229738a3b9, 0xd8988a9f1cc4a61, 0x2dd0712657fe8, 0x9dd6a3364c358319, 13}, {_M64, _M64, _M64 - 1, 1, 42}, } { - testMul("Mul64", Mul64, a.x, a.y, a.hi, a.lo) - testMul("Mul64 symmetric", Mul64, a.y, a.x, a.hi, a.lo) - testDiv("Div64", Div64, a.hi, a.lo+a.r, a.y, a.x, a.r) - testDiv("Div64 symmetric", Div64, a.hi, a.lo+a.r, a.x, a.y, a.r) + testMul("Mul64", bits.Mul64, a.x, a.y, a.hi, a.lo) + testMul("Mul64 symmetric", bits.Mul64, a.y, a.x, a.hi, a.lo) + testDiv("Div64", bits.Div64, a.hi, a.lo+a.r, a.y, a.x, a.r) + testDiv("Div64 symmetric", bits.Div64, a.hi, a.lo+a.r, a.x, a.y, a.r) // The above code can't test intrinsic implementation, because the passed function is not called directly. // The following code uses a closure to test the intrinsic version in case the function is intrinsified. - testMul("Mul64 intrinsic", func(x, y uint64) (uint64, uint64) { return Mul64(x, y) }, a.x, a.y, a.hi, a.lo) - testMul("Mul64 intrinsic symmetric", func(x, y uint64) (uint64, uint64) { return Mul64(x, y) }, a.y, a.x, a.hi, a.lo) - testDiv("Div64 intrinsic", func(hi, lo, y uint64) (uint64, uint64) { return Div64(hi, lo, y) }, a.hi, a.lo+a.r, a.y, a.x, a.r) - testDiv("Div64 intrinsic symmetric", func(hi, lo, y uint64) (uint64, uint64) { return Div64(hi, lo, y) }, a.hi, a.lo+a.r, a.x, a.y, a.r) + testMul("Mul64 intrinsic", func(x, y uint64) (uint64, uint64) { return bits.Mul64(x, y) }, a.x, a.y, a.hi, a.lo) + testMul("Mul64 intrinsic symmetric", func(x, y uint64) (uint64, uint64) { return bits.Mul64(x, y) }, a.y, a.x, a.hi, a.lo) + testDiv("Div64 intrinsic", func(hi, lo, y uint64) (uint64, uint64) { return bits.Div64(hi, lo, y) }, a.hi, a.lo+a.r, a.y, a.x, a.r) + testDiv("Div64 intrinsic symmetric", func(hi, lo, y uint64) (uint64, uint64) { return bits.Div64(hi, lo, y) }, a.hi, a.lo+a.r, a.x, a.y, a.r) } } @@ -1020,11 +1021,11 @@ func TestDivPanicOverflow(t *testing.T) { defer func() { if err := recover(); err == nil { t.Error("Div should have panicked when y<=hi") - } else if err != overflowError { - t.Errorf("Div expected panic: %q, got: %v ", overflowError, err) + } else if err != bits.OverflowError { + t.Errorf("Div expected panic: %q, got: %v ", bits.OverflowError, err) } }() - q, r := Div(1, 0, 1) + q, r := bits.Div(1, 0, 1) t.Errorf("undefined q, r = %v, %v calculated when Div should have panicked", q, r) } @@ -1033,11 +1034,11 @@ func TestDiv32PanicOverflow(t *testing.T) { defer func() { if err := recover(); err == nil { t.Error("Div32 should have panicked when y<=hi") - } else if err != overflowError { - t.Errorf("Div32 expected panic: %q, got: %v ", overflowError, err) + } else if err != bits.OverflowError { + t.Errorf("Div32 expected panic: %q, got: %v ", bits.OverflowError, err) } }() - q, r := Div32(1, 0, 1) + q, r := bits.Div32(1, 0, 1) t.Errorf("undefined q, r = %v, %v calculated when Div32 should have panicked", q, r) } @@ -1046,11 +1047,11 @@ func TestDiv64PanicOverflow(t *testing.T) { defer func() { if err := recover(); err == nil { t.Error("Div64 should have panicked when y<=hi") - } else if err != overflowError { - t.Errorf("Div64 expected panic: %q, got: %v ", overflowError, err) + } else if err != bits.OverflowError { + t.Errorf("Div64 expected panic: %q, got: %v ", bits.OverflowError, err) } }() - q, r := Div64(1, 0, 1) + q, r := bits.Div64(1, 0, 1) t.Errorf("undefined q, r = %v, %v calculated when Div64 should have panicked", q, r) } @@ -1059,11 +1060,11 @@ func TestDivPanicZero(t *testing.T) { defer func() { if err := recover(); err == nil { t.Error("Div should have panicked when y==0") - } else if err != divideError { - t.Errorf("Div expected panic: %q, got: %q ", divideError, err) + } else if err != bits.DivideError { + t.Errorf("Div expected panic: %q, got: %q ", bits.DivideError, err) } }() - q, r := Div(1, 1, 0) + q, r := bits.Div(1, 1, 0) t.Errorf("undefined q, r = %v, %v calculated when Div should have panicked", q, r) } @@ -1072,11 +1073,11 @@ func TestDiv32PanicZero(t *testing.T) { defer func() { if err := recover(); err == nil { t.Error("Div32 should have panicked when y==0") - } else if err != divideError { - t.Errorf("Div32 expected panic: %q, got: %q ", divideError, err) + } else if err != bits.DivideError { + t.Errorf("Div32 expected panic: %q, got: %q ", bits.DivideError, err) } }() - q, r := Div32(1, 1, 0) + q, r := bits.Div32(1, 1, 0) t.Errorf("undefined q, r = %v, %v calculated when Div32 should have panicked", q, r) } @@ -1085,11 +1086,11 @@ func TestDiv64PanicZero(t *testing.T) { defer func() { if err := recover(); err == nil { t.Error("Div64 should have panicked when y==0") - } else if err != divideError { - t.Errorf("Div64 expected panic: %q, got: %q ", divideError, err) + } else if err != bits.DivideError { + t.Errorf("Div64 expected panic: %q, got: %q ", bits.DivideError, err) } }() - q, r := Div64(1, 1, 0) + q, r := bits.Div64(1, 1, 0) t.Errorf("undefined q, r = %v, %v calculated when Div64 should have panicked", q, r) } @@ -1098,8 +1099,8 @@ func TestRem32(t *testing.T) { // same as the rem returned by Div32 hi, lo, y := uint32(510510), uint32(9699690), uint32(510510+1) // ensure hi < y for i := 0; i < 1000; i++ { - r := Rem32(hi, lo, y) - _, r2 := Div32(hi, lo, y) + r := bits.Rem32(hi, lo, y) + _, r2 := bits.Div32(hi, lo, y) if r != r2 { t.Errorf("Rem32(%v, %v, %v) returned %v, but Div32 returned rem %v", hi, lo, y, r, r2) } @@ -1111,8 +1112,8 @@ func TestRem32Overflow(t *testing.T) { // To trigger a quotient overflow, we need y <= hi hi, lo, y := uint32(510510), uint32(9699690), uint32(7) for i := 0; i < 1000; i++ { - r := Rem32(hi, lo, y) - _, r2 := Div64(0, uint64(hi)<<32|uint64(lo), uint64(y)) + r := bits.Rem32(hi, lo, y) + _, r2 := bits.Div64(0, uint64(hi)<<32|uint64(lo), uint64(y)) if r != uint32(r2) { t.Errorf("Rem32(%v, %v, %v) returned %v, but Div64 returned rem %v", hi, lo, y, r, r2) } @@ -1125,8 +1126,8 @@ func TestRem64(t *testing.T) { // same as the rem returned by Div64 hi, lo, y := uint64(510510), uint64(9699690), uint64(510510+1) // ensure hi < y for i := 0; i < 1000; i++ { - r := Rem64(hi, lo, y) - _, r2 := Div64(hi, lo, y) + r := bits.Rem64(hi, lo, y) + _, r2 := bits.Div64(hi, lo, y) if r != r2 { t.Errorf("Rem64(%v, %v, %v) returned %v, but Div64 returned rem %v", hi, lo, y, r, r2) } @@ -1155,7 +1156,7 @@ func TestRem64Overflow(t *testing.T) { if rt.hi < rt.y { t.Fatalf("Rem64(%v, %v, %v) is not a test with quo overflow", rt.hi, rt.lo, rt.y) } - rem := Rem64(rt.hi, rt.lo, rt.y) + rem := bits.Rem64(rt.hi, rt.lo, rt.y) if rem != rt.rem { t.Errorf("Rem64(%v, %v, %v) returned %v, wanted %v", rt.hi, rt.lo, rt.y, rem, rt.rem) @@ -1166,7 +1167,7 @@ func TestRem64Overflow(t *testing.T) { func BenchmarkAdd(b *testing.B) { var z, c uint for i := 0; i < b.N; i++ { - z, c = Add(uint(Input), uint(i), c) + z, c = bits.Add(uint(Input), uint(i), c) } Output = int(z + c) } @@ -1174,7 +1175,7 @@ func BenchmarkAdd(b *testing.B) { func BenchmarkAdd32(b *testing.B) { var z, c uint32 for i := 0; i < b.N; i++ { - z, c = Add32(uint32(Input), uint32(i), c) + z, c = bits.Add32(uint32(Input), uint32(i), c) } Output = int(z + c) } @@ -1182,7 +1183,7 @@ func BenchmarkAdd32(b *testing.B) { func BenchmarkAdd64(b *testing.B) { var z, c uint64 for i := 0; i < b.N; i++ { - z, c = Add64(uint64(Input), uint64(i), c) + z, c = bits.Add64(uint64(Input), uint64(i), c) } Output = int(z + c) } @@ -1194,10 +1195,10 @@ func BenchmarkAdd64multiple(b *testing.B) { z3 := uint64(Input) for i := 0; i < b.N; i++ { var c uint64 - z0, c = Add64(z0, uint64(i), c) - z1, c = Add64(z1, uint64(i), c) - z2, c = Add64(z2, uint64(i), c) - z3, _ = Add64(z3, uint64(i), c) + z0, c = bits.Add64(z0, uint64(i), c) + z1, c = bits.Add64(z1, uint64(i), c) + z2, c = bits.Add64(z2, uint64(i), c) + z3, _ = bits.Add64(z3, uint64(i), c) } Output = int(z0 + z1 + z2 + z3) } @@ -1205,7 +1206,7 @@ func BenchmarkAdd64multiple(b *testing.B) { func BenchmarkSub(b *testing.B) { var z, c uint for i := 0; i < b.N; i++ { - z, c = Sub(uint(Input), uint(i), c) + z, c = bits.Sub(uint(Input), uint(i), c) } Output = int(z + c) } @@ -1213,7 +1214,7 @@ func BenchmarkSub(b *testing.B) { func BenchmarkSub32(b *testing.B) { var z, c uint32 for i := 0; i < b.N; i++ { - z, c = Sub32(uint32(Input), uint32(i), c) + z, c = bits.Sub32(uint32(Input), uint32(i), c) } Output = int(z + c) } @@ -1221,7 +1222,7 @@ func BenchmarkSub32(b *testing.B) { func BenchmarkSub64(b *testing.B) { var z, c uint64 for i := 0; i < b.N; i++ { - z, c = Sub64(uint64(Input), uint64(i), c) + z, c = bits.Sub64(uint64(Input), uint64(i), c) } Output = int(z + c) } @@ -1233,10 +1234,10 @@ func BenchmarkSub64multiple(b *testing.B) { z3 := uint64(Input) for i := 0; i < b.N; i++ { var c uint64 - z0, c = Sub64(z0, uint64(i), c) - z1, c = Sub64(z1, uint64(i), c) - z2, c = Sub64(z2, uint64(i), c) - z3, _ = Sub64(z3, uint64(i), c) + z0, c = bits.Sub64(z0, uint64(i), c) + z1, c = bits.Sub64(z1, uint64(i), c) + z2, c = bits.Sub64(z2, uint64(i), c) + z3, _ = bits.Sub64(z3, uint64(i), c) } Output = int(z0 + z1 + z2 + z3) } @@ -1244,7 +1245,7 @@ func BenchmarkSub64multiple(b *testing.B) { func BenchmarkMul(b *testing.B) { var hi, lo uint for i := 0; i < b.N; i++ { - hi, lo = Mul(uint(Input), uint(i)) + hi, lo = bits.Mul(uint(Input), uint(i)) } Output = int(hi + lo) } @@ -1252,7 +1253,7 @@ func BenchmarkMul(b *testing.B) { func BenchmarkMul32(b *testing.B) { var hi, lo uint32 for i := 0; i < b.N; i++ { - hi, lo = Mul32(uint32(Input), uint32(i)) + hi, lo = bits.Mul32(uint32(Input), uint32(i)) } Output = int(hi + lo) } @@ -1260,7 +1261,7 @@ func BenchmarkMul32(b *testing.B) { func BenchmarkMul64(b *testing.B) { var hi, lo uint64 for i := 0; i < b.N; i++ { - hi, lo = Mul64(uint64(Input), uint64(i)) + hi, lo = bits.Mul64(uint64(Input), uint64(i)) } Output = int(hi + lo) } @@ -1268,7 +1269,7 @@ func BenchmarkMul64(b *testing.B) { func BenchmarkDiv(b *testing.B) { var q, r uint for i := 0; i < b.N; i++ { - q, r = Div(1, uint(i), uint(Input)) + q, r = bits.Div(1, uint(i), uint(Input)) } Output = int(q + r) } @@ -1276,7 +1277,7 @@ func BenchmarkDiv(b *testing.B) { func BenchmarkDiv32(b *testing.B) { var q, r uint32 for i := 0; i < b.N; i++ { - q, r = Div32(1, uint32(i), uint32(Input)) + q, r = bits.Div32(1, uint32(i), uint32(Input)) } Output = int(q + r) } @@ -1284,7 +1285,7 @@ func BenchmarkDiv32(b *testing.B) { func BenchmarkDiv64(b *testing.B) { var q, r uint64 for i := 0; i < b.N; i++ { - q, r = Div64(1, uint64(i), uint64(Input)) + q, r = bits.Div64(1, uint64(i), uint64(Input)) } Output = int(q + r) } diff --git a/gnovm/stdlibs/math/bits/export_test.gno b/gnovm/stdlibs/math/bits/export_test.gno index 8c6f9332cca..a0165163d8f 100644 --- a/gnovm/stdlibs/math/bits/export_test.gno +++ b/gnovm/stdlibs/math/bits/export_test.gno @@ -4,4 +4,9 @@ package bits +// exported for tests + const DeBruijn64 = deBruijn64 + +var OverflowError = overflowError +var DivideError = divideError diff --git a/gnovm/stdlibs/strconv/atob_test.gno b/gnovm/stdlibs/strconv/atob_test.gno index 39746f8953d..6e6d34e8320 100644 --- a/gnovm/stdlibs/strconv/atob_test.gno +++ b/gnovm/stdlibs/strconv/atob_test.gno @@ -2,10 +2,11 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package strconv +package strconv_test import ( "bytes" + "strconv" "testing" ) @@ -16,8 +17,8 @@ type atobTest struct { } var atobtests = []atobTest{ - {"", false, ErrSyntax}, - {"asdf", false, ErrSyntax}, + {"", false, strconv.ErrSyntax}, + {"asdf", false, strconv.ErrSyntax}, {"0", false, nil}, {"f", false, nil}, {"F", false, nil}, @@ -34,14 +35,14 @@ var atobtests = []atobTest{ func TestParseBool(t *testing.T) { for _, test := range atobtests { - b, e := ParseBool(test.in) + b, e := strconv.ParseBool(test.in) if test.err != nil { // expect an error if e == nil { t.Errorf("ParseBool(%s) = nil; want %s", test.in, test.err) } else { // NumError assertion must succeed; it's the only thing we return. - if e.(*NumError).Err != test.err { + if e.(*strconv.NumError).Err != test.err { t.Errorf("ParseBool(%s) = %s; want %s", test.in, e, test.err) } } @@ -63,7 +64,7 @@ var boolString = map[bool]string{ func TestFormatBool(t *testing.T) { for b, s := range boolString { - if f := FormatBool(b); f != s { + if f := strconv.FormatBool(b); f != s { t.Errorf("FormatBool(%v) = %q; want %q", b, f, s) } } @@ -82,7 +83,7 @@ var appendBoolTests = []appendBoolTest{ func TestAppendBool(t *testing.T) { for _, test := range appendBoolTests { - b := AppendBool(test.in, test.b) + b := strconv.AppendBool(test.in, test.b) if !bytes.Equal(b, test.out) { t.Errorf("AppendBool(%q, %v) = %q; want %q", test.in, test.b, b, test.out) } diff --git a/gnovm/stdlibs/strconv/atof_test.gno b/gnovm/stdlibs/strconv/atof_test.gno index 29d9e4e2f4b..63cf659018b 100644 --- a/gnovm/stdlibs/strconv/atof_test.gno +++ b/gnovm/stdlibs/strconv/atof_test.gno @@ -2,13 +2,14 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package strconv +package strconv_test // XXX: changed not to use dot imports import ( "math" "math/rand" + "strconv" "strings" "testing" ) @@ -20,11 +21,11 @@ type atofTest struct { } var atoftests = []atofTest{ - {"", "0", ErrSyntax}, + {"", "0", strconv.ErrSyntax}, {"1", "1", nil}, {"+1", "1", nil}, - {"1x", "0", ErrSyntax}, - {"1.1.", "0", ErrSyntax}, + {"1x", "0", strconv.ErrSyntax}, + {"1.1.", "0", strconv.ErrSyntax}, {"1e23", "1e+23", nil}, {"1E23", "1e+23", nil}, {"100000000000000000000000", "1e+23", nil}, @@ -55,8 +56,8 @@ var atoftests = []atofTest{ {"-0x2p3", "-16", nil}, {"0x0.fp4", "15", nil}, {"0x0.fp0", "0.9375", nil}, - {"0x1e2", "0", ErrSyntax}, - {"1p2", "0", ErrSyntax}, + {"0x1e2", "0", strconv.ErrSyntax}, + {"1p2", "0", strconv.ErrSyntax}, // zeros {"0", "0", nil}, @@ -131,16 +132,16 @@ var atoftests = []atofTest{ {"-0x.1fffffffffffffp1027", "-1.7976931348623157e+308", nil}, // next float64 - too large - {"1.7976931348623159e308", "+Inf", ErrRange}, - {"-1.7976931348623159e308", "-Inf", ErrRange}, - {"0x1p1024", "+Inf", ErrRange}, - {"-0x1p1024", "-Inf", ErrRange}, - {"0x2p1023", "+Inf", ErrRange}, - {"-0x2p1023", "-Inf", ErrRange}, - {"0x.1p1028", "+Inf", ErrRange}, - {"-0x.1p1028", "-Inf", ErrRange}, - {"0x.2p1027", "+Inf", ErrRange}, - {"-0x.2p1027", "-Inf", ErrRange}, + {"1.7976931348623159e308", "+Inf", strconv.ErrRange}, + {"-1.7976931348623159e308", "-Inf", strconv.ErrRange}, + {"0x1p1024", "+Inf", strconv.ErrRange}, + {"-0x1p1024", "-Inf", strconv.ErrRange}, + {"0x2p1023", "+Inf", strconv.ErrRange}, + {"-0x2p1023", "-Inf", strconv.ErrRange}, + {"0x.1p1028", "+Inf", strconv.ErrRange}, + {"-0x.1p1028", "-Inf", strconv.ErrRange}, + {"0x.2p1027", "+Inf", strconv.ErrRange}, + {"-0x.2p1027", "-Inf", strconv.ErrRange}, // the border is ...158079 // borderline - okay @@ -149,34 +150,34 @@ var atoftests = []atofTest{ {"0x1.fffffffffffff7fffp1023", "1.7976931348623157e+308", nil}, {"-0x1.fffffffffffff7fffp1023", "-1.7976931348623157e+308", nil}, // borderline - too large - {"1.797693134862315808e308", "+Inf", ErrRange}, - {"-1.797693134862315808e308", "-Inf", ErrRange}, - {"0x1.fffffffffffff8p1023", "+Inf", ErrRange}, - {"-0x1.fffffffffffff8p1023", "-Inf", ErrRange}, - {"0x1fffffffffffff.8p+971", "+Inf", ErrRange}, - {"-0x1fffffffffffff8p+967", "-Inf", ErrRange}, - {"0x.1fffffffffffff8p1027", "+Inf", ErrRange}, - {"-0x.1fffffffffffff9p1027", "-Inf", ErrRange}, + {"1.797693134862315808e308", "+Inf", strconv.ErrRange}, + {"-1.797693134862315808e308", "-Inf", strconv.ErrRange}, + {"0x1.fffffffffffff8p1023", "+Inf", strconv.ErrRange}, + {"-0x1.fffffffffffff8p1023", "-Inf", strconv.ErrRange}, + {"0x1fffffffffffff.8p+971", "+Inf", strconv.ErrRange}, + {"-0x1fffffffffffff8p+967", "-Inf", strconv.ErrRange}, + {"0x.1fffffffffffff8p1027", "+Inf", strconv.ErrRange}, + {"-0x.1fffffffffffff9p1027", "-Inf", strconv.ErrRange}, // a little too large {"1e308", "1e+308", nil}, - {"2e308", "+Inf", ErrRange}, - {"1e309", "+Inf", ErrRange}, - {"0x1p1025", "+Inf", ErrRange}, + {"2e308", "+Inf", strconv.ErrRange}, + {"1e309", "+Inf", strconv.ErrRange}, + {"0x1p1025", "+Inf", strconv.ErrRange}, // way too large - {"1e310", "+Inf", ErrRange}, - {"-1e310", "-Inf", ErrRange}, - {"1e400", "+Inf", ErrRange}, - {"-1e400", "-Inf", ErrRange}, - {"1e400000", "+Inf", ErrRange}, - {"-1e400000", "-Inf", ErrRange}, - {"0x1p1030", "+Inf", ErrRange}, - {"0x1p2000", "+Inf", ErrRange}, - {"0x1p2000000000", "+Inf", ErrRange}, - {"-0x1p1030", "-Inf", ErrRange}, - {"-0x1p2000", "-Inf", ErrRange}, - {"-0x1p2000000000", "-Inf", ErrRange}, + {"1e310", "+Inf", strconv.ErrRange}, + {"-1e310", "-Inf", strconv.ErrRange}, + {"1e400", "+Inf", strconv.ErrRange}, + {"-1e400", "-Inf", strconv.ErrRange}, + {"1e400000", "+Inf", strconv.ErrRange}, + {"-1e400000", "-Inf", strconv.ErrRange}, + {"0x1p1030", "+Inf", strconv.ErrRange}, + {"0x1p2000", "+Inf", strconv.ErrRange}, + {"0x1p2000000000", "+Inf", strconv.ErrRange}, + {"-0x1p1030", "-Inf", strconv.ErrRange}, + {"-0x1p2000", "-Inf", strconv.ErrRange}, + {"-0x1p2000000000", "-Inf", strconv.ErrRange}, // denormalized {"1e-305", "1e-305", nil}, @@ -238,29 +239,29 @@ var atoftests = []atofTest{ // try to overflow exponent {"1e-4294967296", "0", nil}, - {"1e+4294967296", "+Inf", ErrRange}, + {"1e+4294967296", "+Inf", strconv.ErrRange}, {"1e-18446744073709551616", "0", nil}, - {"1e+18446744073709551616", "+Inf", ErrRange}, + {"1e+18446744073709551616", "+Inf", strconv.ErrRange}, {"0x1p-4294967296", "0", nil}, - {"0x1p+4294967296", "+Inf", ErrRange}, + {"0x1p+4294967296", "+Inf", strconv.ErrRange}, {"0x1p-18446744073709551616", "0", nil}, - {"0x1p+18446744073709551616", "+Inf", ErrRange}, + {"0x1p+18446744073709551616", "+Inf", strconv.ErrRange}, // Parse errors - {"1e", "0", ErrSyntax}, - {"1e-", "0", ErrSyntax}, - {".e-1", "0", ErrSyntax}, - {"1\x00.2", "0", ErrSyntax}, - {"0x", "0", ErrSyntax}, - {"0x.", "0", ErrSyntax}, - {"0x1", "0", ErrSyntax}, - {"0x.1", "0", ErrSyntax}, - {"0x1p", "0", ErrSyntax}, - {"0x.1p", "0", ErrSyntax}, - {"0x1p+", "0", ErrSyntax}, - {"0x.1p+", "0", ErrSyntax}, - {"0x1p-", "0", ErrSyntax}, - {"0x.1p-", "0", ErrSyntax}, + {"1e", "0", strconv.ErrSyntax}, + {"1e-", "0", strconv.ErrSyntax}, + {".e-1", "0", strconv.ErrSyntax}, + {"1\x00.2", "0", strconv.ErrSyntax}, + {"0x", "0", strconv.ErrSyntax}, + {"0x.", "0", strconv.ErrSyntax}, + {"0x1", "0", strconv.ErrSyntax}, + {"0x.1", "0", strconv.ErrSyntax}, + {"0x1p", "0", strconv.ErrSyntax}, + {"0x.1p", "0", strconv.ErrSyntax}, + {"0x1p+", "0", strconv.ErrSyntax}, + {"0x.1p+", "0", strconv.ErrSyntax}, + {"0x1p-", "0", strconv.ErrSyntax}, + {"0x.1p-", "0", strconv.ErrSyntax}, {"0x1p+2", "4", nil}, {"0x.1p+2", "0.25", nil}, {"0x1p-2", "0.25", nil}, @@ -309,40 +310,40 @@ var atoftests = []atofTest{ // Underscores. {"1_23.50_0_0e+1_2", "1.235e+14", nil}, - {"-_123.5e+12", "0", ErrSyntax}, - {"+_123.5e+12", "0", ErrSyntax}, - {"_123.5e+12", "0", ErrSyntax}, - {"1__23.5e+12", "0", ErrSyntax}, - {"123_.5e+12", "0", ErrSyntax}, - {"123._5e+12", "0", ErrSyntax}, - {"123.5_e+12", "0", ErrSyntax}, - {"123.5__0e+12", "0", ErrSyntax}, - {"123.5e_+12", "0", ErrSyntax}, - {"123.5e+_12", "0", ErrSyntax}, - {"123.5e_-12", "0", ErrSyntax}, - {"123.5e-_12", "0", ErrSyntax}, - {"123.5e+1__2", "0", ErrSyntax}, - {"123.5e+12_", "0", ErrSyntax}, + {"-_123.5e+12", "0", strconv.ErrSyntax}, + {"+_123.5e+12", "0", strconv.ErrSyntax}, + {"_123.5e+12", "0", strconv.ErrSyntax}, + {"1__23.5e+12", "0", strconv.ErrSyntax}, + {"123_.5e+12", "0", strconv.ErrSyntax}, + {"123._5e+12", "0", strconv.ErrSyntax}, + {"123.5_e+12", "0", strconv.ErrSyntax}, + {"123.5__0e+12", "0", strconv.ErrSyntax}, + {"123.5e_+12", "0", strconv.ErrSyntax}, + {"123.5e+_12", "0", strconv.ErrSyntax}, + {"123.5e_-12", "0", strconv.ErrSyntax}, + {"123.5e-_12", "0", strconv.ErrSyntax}, + {"123.5e+1__2", "0", strconv.ErrSyntax}, + {"123.5e+12_", "0", strconv.ErrSyntax}, {"0x_1_2.3_4_5p+1_2", "74565", nil}, - {"-_0x12.345p+12", "0", ErrSyntax}, - {"+_0x12.345p+12", "0", ErrSyntax}, - {"_0x12.345p+12", "0", ErrSyntax}, - {"0x__12.345p+12", "0", ErrSyntax}, - {"0x1__2.345p+12", "0", ErrSyntax}, - {"0x12_.345p+12", "0", ErrSyntax}, - {"0x12._345p+12", "0", ErrSyntax}, - {"0x12.3__45p+12", "0", ErrSyntax}, - {"0x12.345_p+12", "0", ErrSyntax}, - {"0x12.345p_+12", "0", ErrSyntax}, - {"0x12.345p+_12", "0", ErrSyntax}, - {"0x12.345p_-12", "0", ErrSyntax}, - {"0x12.345p-_12", "0", ErrSyntax}, - {"0x12.345p+1__2", "0", ErrSyntax}, - {"0x12.345p+12_", "0", ErrSyntax}, - - {"1e100x", "0", ErrSyntax}, - {"1e1000x", "0", ErrSyntax}, + {"-_0x12.345p+12", "0", strconv.ErrSyntax}, + {"+_0x12.345p+12", "0", strconv.ErrSyntax}, + {"_0x12.345p+12", "0", strconv.ErrSyntax}, + {"0x__12.345p+12", "0", strconv.ErrSyntax}, + {"0x1__2.345p+12", "0", strconv.ErrSyntax}, + {"0x12_.345p+12", "0", strconv.ErrSyntax}, + {"0x12._345p+12", "0", strconv.ErrSyntax}, + {"0x12.3__45p+12", "0", strconv.ErrSyntax}, + {"0x12.345_p+12", "0", strconv.ErrSyntax}, + {"0x12.345p_+12", "0", strconv.ErrSyntax}, + {"0x12.345p+_12", "0", strconv.ErrSyntax}, + {"0x12.345p_-12", "0", strconv.ErrSyntax}, + {"0x12.345p-_12", "0", strconv.ErrSyntax}, + {"0x12.345p+1__2", "0", strconv.ErrSyntax}, + {"0x12.345p+12_", "0", strconv.ErrSyntax}, + + {"1e100x", "0", strconv.ErrSyntax}, + {"1e1000x", "0", strconv.ErrSyntax}, } var atof32tests = []atofTest{ @@ -374,10 +375,10 @@ var atof32tests = []atofTest{ {"-340282346638528859811704183484516925440", "-3.4028235e+38", nil}, {"-0x.ffffffp128", "-3.4028235e+38", nil}, // next float32 - too large - {"3.4028236e38", "+Inf", ErrRange}, - {"-3.4028236e38", "-Inf", ErrRange}, - {"0x1.0p128", "+Inf", ErrRange}, - {"-0x1.0p128", "-Inf", ErrRange}, + {"3.4028236e38", "+Inf", strconv.ErrRange}, + {"-3.4028236e38", "-Inf", strconv.ErrRange}, + {"0x1.0p128", "+Inf", strconv.ErrRange}, + {"-0x1.0p128", "-Inf", strconv.ErrRange}, // the border is 3.40282356779...e+38 // borderline - okay {"3.402823567e38", "3.4028235e+38", nil}, @@ -385,10 +386,10 @@ var atof32tests = []atofTest{ {"0x.ffffff7fp128", "3.4028235e+38", nil}, {"-0x.ffffff7fp128", "-3.4028235e+38", nil}, // borderline - too large - {"3.4028235678e38", "+Inf", ErrRange}, - {"-3.4028235678e38", "-Inf", ErrRange}, - {"0x.ffffff8p128", "+Inf", ErrRange}, - {"-0x.ffffff8p128", "-Inf", ErrRange}, + {"3.4028235678e38", "+Inf", strconv.ErrRange}, + {"-3.4028235678e38", "-Inf", strconv.ErrRange}, + {"0x.ffffff8p128", "+Inf", strconv.ErrRange}, + {"-0x.ffffff8p128", "-Inf", strconv.ErrRange}, // Denormals: less than 2^-126 {"1e-38", "1e-38", nil}, @@ -454,13 +455,13 @@ func initAtofOnce() { for i := range atoftests { test := &atoftests[i] if test.err != nil { - test.err = &NumError{"ParseFloat", test.in, test.err} + test.err = &strconv.NumError{"ParseFloat", test.in, test.err} } } for i := range atof32tests { test := &atof32tests[i] if test.err != nil { - test.err = &NumError{"ParseFloat", test.in, test.err} + test.err = &strconv.NumError{"ParseFloat", test.in, test.err} } } @@ -473,19 +474,19 @@ func initAtofOnce() { for i := range atofRandomTests { n := uint64(rand.Uint32())<<32 | uint64(rand.Uint32()) x := math.Float64frombits(n) - s := FormatFloat(x, 'g', -1, 64) + s := strconv.FormatFloat(x, 'g', -1, 64) atofRandomTests[i] = atofSimpleTest{x, s} } for i := range benchmarksRandomBits { bits := uint64(rand.Uint32())<<32 | uint64(rand.Uint32()) x := math.Float64frombits(bits) - benchmarksRandomBits[i] = FormatFloat(x, 'g', -1, 64) + benchmarksRandomBits[i] = strconv.FormatFloat(x, 'g', -1, 64) } for i := range benchmarksRandomNormal { x := rand.NormFloat64() - benchmarksRandomNormal[i] = FormatFloat(x, 'g', -1, 64) + benchmarksRandomNormal[i] = strconv.FormatFloat(x, 'g', -1, 64) } } @@ -500,7 +501,7 @@ func TestParseFloatPrefix(t *testing.T) { // correctly as "inf" with suffix. for _, suffix := range []string{" ", "q", "+", "-", "<", "=", ">", "(", ")", "i", "init"} { in := test.in + suffix - _, n, err := ParseFloatPrefix(in, 64) + _, n, err := strconv.ParseFloatPrefix(in, 64) if err != nil { t.Errorf("ParseFloatPrefix(%q, 64): err = %v; want no error", in, err) } @@ -530,24 +531,24 @@ func printError(err error) string { func testAtof(t *testing.T, opt bool) { initAtof() - oldopt := SetOptimize(opt) + oldopt := strconv.SetOptimize(opt) for i := 0; i < len(atoftests); i++ { test := &atoftests[i] - out, err := ParseFloat(test.in, 64) - outs := FormatFloat(out, 'g', -1, 64) + out, err := strconv.ParseFloat(test.in, 64) + outs := strconv.FormatFloat(out, 'g', -1, 64) if outs != test.out || !errEqual(err, test.err) { t.Errorf("ParseFloat(%v, 64) = %v, %v want %v, %v", test.in, out, printError(err), test.out, printError(test.err)) } if float64(float32(out)) == out { - out, err := ParseFloat(test.in, 32) + out, err := strconv.ParseFloat(test.in, 32) out32 := float32(out) if float64(out32) != out { t.Errorf("ParseFloat(%v, 32) = %v, not a float32 (closest is %v)", test.in, out, float64(out32)) continue } - outs := FormatFloat(float64(out32), 'g', -1, 32) + outs := strconv.FormatFloat(float64(out32), 'g', -1, 32) if outs != test.out || !errEqual(err, test.err) { t.Errorf("ParseFloat(%v, 32) = %v, %v want %v, %v # %v", test.in, out32, printError(err), test.out, printError(test.err), out) @@ -555,19 +556,19 @@ func testAtof(t *testing.T, opt bool) { } } for _, test := range atof32tests { - out, err := ParseFloat(test.in, 32) + out, err := strconv.ParseFloat(test.in, 32) out32 := float32(out) if float64(out32) != out { t.Errorf("ParseFloat(%v, 32) = %v, not a float32 (closest is %v)", test.in, out, float64(out32)) continue } - outs := FormatFloat(float64(out32), 'g', -1, 32) + outs := strconv.FormatFloat(float64(out32), 'g', -1, 32) if outs != test.out || !errEqual(err, test.err) { t.Errorf("ParseFloat(%v, 32) = %v, %v want %v, %v # %v", test.in, out32, printError(err), test.out, printError(test.err), out) } } - SetOptimize(oldopt) + strconv.SetOptimize(oldopt) } func TestAtof(t *testing.T) { testAtof(t, true) } @@ -577,7 +578,7 @@ func TestAtofSlow(t *testing.T) { testAtof(t, false) } func TestAtofRandom(t *testing.T) { initAtof() for _, test := range atofRandomTests { - x, _ := ParseFloat(test.s, 64) + x, _ := strconv.ParseFloat(test.s, 64) switch { default: t.Errorf("number %s badly parsed as %b (expected %b)", test.s, x, test.x) @@ -604,25 +605,25 @@ var roundTripCases = []struct { func TestRoundTrip(t *testing.T) { for _, tt := range roundTripCases { - old := SetOptimize(false) - s := FormatFloat(tt.f, 'g', -1, 64) + old := strconv.SetOptimize(false) + s := strconv.FormatFloat(tt.f, 'g', -1, 64) if s != tt.s { t.Errorf("no-opt FormatFloat(%b) = %s, want %s", tt.f, s, tt.s) } - f, err := ParseFloat(tt.s, 64) + f, err := strconv.ParseFloat(tt.s, 64) if f != tt.f || err != nil { t.Errorf("no-opt ParseFloat(%s) = %b, %v want %b, nil", tt.s, f, err, tt.f) } - SetOptimize(true) - s = FormatFloat(tt.f, 'g', -1, 64) + strconv.SetOptimize(true) + s = strconv.FormatFloat(tt.f, 'g', -1, 64) if s != tt.s { t.Errorf("opt FormatFloat(%b) = %s, want %s", tt.f, s, tt.s) } - f, err = ParseFloat(tt.s, 64) + f, err = strconv.ParseFloat(tt.s, 64) if f != tt.f || err != nil { t.Errorf("opt ParseFloat(%s) = %b, %v want %b, nil", tt.s, f, err, tt.f) } - SetOptimize(old) + strconv.SetOptimize(old) } } @@ -638,9 +639,9 @@ func TestRoundTrip32(t *testing.T) { if i&1 == 1 { f = -f // negative } - s := FormatFloat(float64(f), 'g', -1, 32) + s := strconv.FormatFloat(float64(f), 'g', -1, 32) - parsed, err := ParseFloat(s, 32) + parsed, err := strconv.ParseFloat(s, 32) parsed32 := float32(parsed) switch { case err != nil: @@ -662,7 +663,7 @@ func TestParseFloatIncorrectBitSize(t *testing.T) { const want = 1.5e308 for _, bitSize := range []int{0, 10, 100, 128} { - f, err := ParseFloat(s, bitSize) + f, err := strconv.ParseFloat(s, bitSize) if err != nil { t.Fatalf("ParseFloat(%q, %d) gave error %s", s, bitSize, err) } @@ -674,25 +675,25 @@ func TestParseFloatIncorrectBitSize(t *testing.T) { func BenchmarkAtof64Decimal(b *testing.B) { for i := 0; i < b.N; i++ { - ParseFloat("33909", 64) + strconv.ParseFloat("33909", 64) } } func BenchmarkAtof64Float(b *testing.B) { for i := 0; i < b.N; i++ { - ParseFloat("339.7784", 64) + strconv.ParseFloat("339.7784", 64) } } func BenchmarkAtof64FloatExp(b *testing.B) { for i := 0; i < b.N; i++ { - ParseFloat("-5.09e75", 64) + strconv.ParseFloat("-5.09e75", 64) } } func BenchmarkAtof64Big(b *testing.B) { for i := 0; i < b.N; i++ { - ParseFloat("123456789123456789123456789", 64) + strconv.ParseFloat("123456789123456789123456789", 64) } } @@ -700,7 +701,7 @@ func BenchmarkAtof64RandomBits(b *testing.B) { initAtof() b.ResetTimer() for i := 0; i < b.N; i++ { - ParseFloat(benchmarksRandomBits[i%1024], 64) + strconv.ParseFloat(benchmarksRandomBits[i%1024], 64) } } @@ -708,7 +709,7 @@ func BenchmarkAtof64RandomFloats(b *testing.B) { initAtof() b.ResetTimer() for i := 0; i < b.N; i++ { - ParseFloat(benchmarksRandomNormal[i%1024], 64) + strconv.ParseFloat(benchmarksRandomNormal[i%1024], 64) } } @@ -716,12 +717,12 @@ func BenchmarkAtof64RandomLongFloats(b *testing.B) { initAtof() samples := make([]string, len(atofRandomTests)) for i, t := range atofRandomTests { - samples[i] = FormatFloat(t.x, 'g', 20, 64) + samples[i] = strconv.FormatFloat(t.x, 'g', 20, 64) } b.ResetTimer() idx := 0 for i := 0; i < b.N; i++ { - ParseFloat(samples[idx], 64) + strconv.ParseFloat(samples[idx], 64) idx++ if idx == len(samples) { idx = 0 @@ -731,19 +732,19 @@ func BenchmarkAtof64RandomLongFloats(b *testing.B) { func BenchmarkAtof32Decimal(b *testing.B) { for i := 0; i < b.N; i++ { - ParseFloat("33909", 32) + strconv.ParseFloat("33909", 32) } } func BenchmarkAtof32Float(b *testing.B) { for i := 0; i < b.N; i++ { - ParseFloat("339.778", 32) + strconv.ParseFloat("339.778", 32) } } func BenchmarkAtof32FloatExp(b *testing.B) { for i := 0; i < b.N; i++ { - ParseFloat("12.3456e32", 32) + strconv.ParseFloat("12.3456e32", 32) } } @@ -752,11 +753,11 @@ func BenchmarkAtof32Random(b *testing.B) { var float32strings [4096]string for i := range float32strings { n = (99991*n + 42) % (0xff << 23) - float32strings[i] = FormatFloat(float64(math.Float32frombits(n)), 'g', -1, 32) + float32strings[i] = strconv.FormatFloat(float64(math.Float32frombits(n)), 'g', -1, 32) } b.ResetTimer() for i := 0; i < b.N; i++ { - ParseFloat(float32strings[i%4096], 32) + strconv.ParseFloat(float32strings[i%4096], 32) } } @@ -765,10 +766,10 @@ func BenchmarkAtof32RandomLong(b *testing.B) { var float32strings [4096]string for i := range float32strings { n = (99991*n + 42) % (0xff << 23) - float32strings[i] = FormatFloat(float64(math.Float32frombits(n)), 'g', 20, 32) + float32strings[i] = strconv.FormatFloat(float64(math.Float32frombits(n)), 'g', 20, 32) } b.ResetTimer() for i := 0; i < b.N; i++ { - ParseFloat(float32strings[i%4096], 32) + strconv.ParseFloat(float32strings[i%4096], 32) } } diff --git a/gnovm/stdlibs/strconv/atoi_test.gno b/gnovm/stdlibs/strconv/atoi_test.gno index cb150628f5c..5dc0577924f 100644 --- a/gnovm/stdlibs/strconv/atoi_test.gno +++ b/gnovm/stdlibs/strconv/atoi_test.gno @@ -2,11 +2,12 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package strconv +package strconv_test import ( "errors" "fmt" + "strconv" "testing" ) @@ -17,23 +18,23 @@ type parseUint64Test struct { } var parseUint64Tests = []parseUint64Test{ - {"", 0, ErrSyntax}, + {"", 0, strconv.ErrSyntax}, {"0", 0, nil}, {"1", 1, nil}, {"12345", 12345, nil}, {"012345", 12345, nil}, - {"12345x", 0, ErrSyntax}, + {"12345x", 0, strconv.ErrSyntax}, {"98765432100", 98765432100, nil}, {"18446744073709551615", 1<<64 - 1, nil}, - {"18446744073709551616", 1<<64 - 1, ErrRange}, - {"18446744073709551620", 1<<64 - 1, ErrRange}, - {"1_2_3_4_5", 0, ErrSyntax}, // base=10 so no underscores allowed - {"_12345", 0, ErrSyntax}, - {"1__2345", 0, ErrSyntax}, - {"12345_", 0, ErrSyntax}, - {"-0", 0, ErrSyntax}, - {"-1", 0, ErrSyntax}, - {"+1", 0, ErrSyntax}, + {"18446744073709551616", 1<<64 - 1, strconv.ErrRange}, + {"18446744073709551620", 1<<64 - 1, strconv.ErrRange}, + {"1_2_3_4_5", 0, strconv.ErrSyntax}, // base=10 so no underscores allowed + {"_12345", 0, strconv.ErrSyntax}, + {"1__2345", 0, strconv.ErrSyntax}, + {"12345_", 0, strconv.ErrSyntax}, + {"-0", 0, strconv.ErrSyntax}, + {"-1", 0, strconv.ErrSyntax}, + {"+1", 0, strconv.ErrSyntax}, } type parseUint64BaseTest struct { @@ -44,91 +45,91 @@ type parseUint64BaseTest struct { } var parseUint64BaseTests = []parseUint64BaseTest{ - {"", 0, 0, ErrSyntax}, + {"", 0, 0, strconv.ErrSyntax}, {"0", 0, 0, nil}, - {"0x", 0, 0, ErrSyntax}, - {"0X", 0, 0, ErrSyntax}, + {"0x", 0, 0, strconv.ErrSyntax}, + {"0X", 0, 0, strconv.ErrSyntax}, {"1", 0, 1, nil}, {"12345", 0, 12345, nil}, {"012345", 0, 012345, nil}, {"0x12345", 0, 0x12345, nil}, {"0X12345", 0, 0x12345, nil}, - {"12345x", 0, 0, ErrSyntax}, - {"0xabcdefg123", 0, 0, ErrSyntax}, - {"123456789abc", 0, 0, ErrSyntax}, + {"12345x", 0, 0, strconv.ErrSyntax}, + {"0xabcdefg123", 0, 0, strconv.ErrSyntax}, + {"123456789abc", 0, 0, strconv.ErrSyntax}, {"98765432100", 0, 98765432100, nil}, {"18446744073709551615", 0, 1<<64 - 1, nil}, - {"18446744073709551616", 0, 1<<64 - 1, ErrRange}, - {"18446744073709551620", 0, 1<<64 - 1, ErrRange}, + {"18446744073709551616", 0, 1<<64 - 1, strconv.ErrRange}, + {"18446744073709551620", 0, 1<<64 - 1, strconv.ErrRange}, {"0xFFFFFFFFFFFFFFFF", 0, 1<<64 - 1, nil}, - {"0x10000000000000000", 0, 1<<64 - 1, ErrRange}, + {"0x10000000000000000", 0, 1<<64 - 1, strconv.ErrRange}, {"01777777777777777777777", 0, 1<<64 - 1, nil}, - {"01777777777777777777778", 0, 0, ErrSyntax}, - {"02000000000000000000000", 0, 1<<64 - 1, ErrRange}, + {"01777777777777777777778", 0, 0, strconv.ErrSyntax}, + {"02000000000000000000000", 0, 1<<64 - 1, strconv.ErrRange}, {"0200000000000000000000", 0, 1 << 61, nil}, - {"0b", 0, 0, ErrSyntax}, - {"0B", 0, 0, ErrSyntax}, + {"0b", 0, 0, strconv.ErrSyntax}, + {"0B", 0, 0, strconv.ErrSyntax}, {"0b101", 0, 5, nil}, {"0B101", 0, 5, nil}, - {"0o", 0, 0, ErrSyntax}, - {"0O", 0, 0, ErrSyntax}, + {"0o", 0, 0, strconv.ErrSyntax}, + {"0O", 0, 0, strconv.ErrSyntax}, {"0o377", 0, 255, nil}, {"0O377", 0, 255, nil}, // underscores allowed with base == 0 only {"1_2_3_4_5", 0, 12345, nil}, // base 0 => 10 - {"_12345", 0, 0, ErrSyntax}, - {"1__2345", 0, 0, ErrSyntax}, - {"12345_", 0, 0, ErrSyntax}, + {"_12345", 0, 0, strconv.ErrSyntax}, + {"1__2345", 0, 0, strconv.ErrSyntax}, + {"12345_", 0, 0, strconv.ErrSyntax}, - {"1_2_3_4_5", 10, 0, ErrSyntax}, // base 10 - {"_12345", 10, 0, ErrSyntax}, - {"1__2345", 10, 0, ErrSyntax}, - {"12345_", 10, 0, ErrSyntax}, + {"1_2_3_4_5", 10, 0, strconv.ErrSyntax}, // base 10 + {"_12345", 10, 0, strconv.ErrSyntax}, + {"1__2345", 10, 0, strconv.ErrSyntax}, + {"12345_", 10, 0, strconv.ErrSyntax}, {"0x_1_2_3_4_5", 0, 0x12345, nil}, // base 0 => 16 - {"_0x12345", 0, 0, ErrSyntax}, - {"0x__12345", 0, 0, ErrSyntax}, - {"0x1__2345", 0, 0, ErrSyntax}, - {"0x1234__5", 0, 0, ErrSyntax}, - {"0x12345_", 0, 0, ErrSyntax}, - - {"1_2_3_4_5", 16, 0, ErrSyntax}, // base 16 - {"_12345", 16, 0, ErrSyntax}, - {"1__2345", 16, 0, ErrSyntax}, - {"1234__5", 16, 0, ErrSyntax}, - {"12345_", 16, 0, ErrSyntax}, + {"_0x12345", 0, 0, strconv.ErrSyntax}, + {"0x__12345", 0, 0, strconv.ErrSyntax}, + {"0x1__2345", 0, 0, strconv.ErrSyntax}, + {"0x1234__5", 0, 0, strconv.ErrSyntax}, + {"0x12345_", 0, 0, strconv.ErrSyntax}, + + {"1_2_3_4_5", 16, 0, strconv.ErrSyntax}, // base 16 + {"_12345", 16, 0, strconv.ErrSyntax}, + {"1__2345", 16, 0, strconv.ErrSyntax}, + {"1234__5", 16, 0, strconv.ErrSyntax}, + {"12345_", 16, 0, strconv.ErrSyntax}, {"0_1_2_3_4_5", 0, 012345, nil}, // base 0 => 8 (0377) - {"_012345", 0, 0, ErrSyntax}, - {"0__12345", 0, 0, ErrSyntax}, - {"01234__5", 0, 0, ErrSyntax}, - {"012345_", 0, 0, ErrSyntax}, + {"_012345", 0, 0, strconv.ErrSyntax}, + {"0__12345", 0, 0, strconv.ErrSyntax}, + {"01234__5", 0, 0, strconv.ErrSyntax}, + {"012345_", 0, 0, strconv.ErrSyntax}, {"0o_1_2_3_4_5", 0, 012345, nil}, // base 0 => 8 (0o377) - {"_0o12345", 0, 0, ErrSyntax}, - {"0o__12345", 0, 0, ErrSyntax}, - {"0o1234__5", 0, 0, ErrSyntax}, - {"0o12345_", 0, 0, ErrSyntax}, + {"_0o12345", 0, 0, strconv.ErrSyntax}, + {"0o__12345", 0, 0, strconv.ErrSyntax}, + {"0o1234__5", 0, 0, strconv.ErrSyntax}, + {"0o12345_", 0, 0, strconv.ErrSyntax}, - {"0_1_2_3_4_5", 8, 0, ErrSyntax}, // base 8 - {"_012345", 8, 0, ErrSyntax}, - {"0__12345", 8, 0, ErrSyntax}, - {"01234__5", 8, 0, ErrSyntax}, - {"012345_", 8, 0, ErrSyntax}, + {"0_1_2_3_4_5", 8, 0, strconv.ErrSyntax}, // base 8 + {"_012345", 8, 0, strconv.ErrSyntax}, + {"0__12345", 8, 0, strconv.ErrSyntax}, + {"01234__5", 8, 0, strconv.ErrSyntax}, + {"012345_", 8, 0, strconv.ErrSyntax}, {"0b_1_0_1", 0, 5, nil}, // base 0 => 2 (0b101) - {"_0b101", 0, 0, ErrSyntax}, - {"0b__101", 0, 0, ErrSyntax}, - {"0b1__01", 0, 0, ErrSyntax}, - {"0b10__1", 0, 0, ErrSyntax}, - {"0b101_", 0, 0, ErrSyntax}, + {"_0b101", 0, 0, strconv.ErrSyntax}, + {"0b__101", 0, 0, strconv.ErrSyntax}, + {"0b1__01", 0, 0, strconv.ErrSyntax}, + {"0b10__1", 0, 0, strconv.ErrSyntax}, + {"0b101_", 0, 0, strconv.ErrSyntax}, - {"1_0_1", 2, 0, ErrSyntax}, // base 2 - {"_101", 2, 0, ErrSyntax}, - {"1_01", 2, 0, ErrSyntax}, - {"10_1", 2, 0, ErrSyntax}, - {"101_", 2, 0, ErrSyntax}, + {"1_0_1", 2, 0, strconv.ErrSyntax}, // base 2 + {"_101", 2, 0, strconv.ErrSyntax}, + {"1_01", 2, 0, strconv.ErrSyntax}, + {"10_1", 2, 0, strconv.ErrSyntax}, + {"101_", 2, 0, strconv.ErrSyntax}, } type parseInt64Test struct { @@ -138,7 +139,7 @@ type parseInt64Test struct { } var parseInt64Tests = []parseInt64Test{ - {"", 0, ErrSyntax}, + {"", 0, strconv.ErrSyntax}, {"0", 0, nil}, {"-0", 0, nil}, {"+0", 0, nil}, @@ -153,16 +154,16 @@ var parseInt64Tests = []parseInt64Test{ {"-98765432100", -98765432100, nil}, {"9223372036854775807", 1<<63 - 1, nil}, {"-9223372036854775807", -(1<<63 - 1), nil}, - {"9223372036854775808", 1<<63 - 1, ErrRange}, + {"9223372036854775808", 1<<63 - 1, strconv.ErrRange}, {"-9223372036854775808", -1 << 63, nil}, - {"9223372036854775809", 1<<63 - 1, ErrRange}, - {"-9223372036854775809", -1 << 63, ErrRange}, - {"-1_2_3_4_5", 0, ErrSyntax}, // base=10 so no underscores allowed - {"-_12345", 0, ErrSyntax}, - {"_12345", 0, ErrSyntax}, - {"1__2345", 0, ErrSyntax}, - {"12345_", 0, ErrSyntax}, - {"123%45", 0, ErrSyntax}, + {"9223372036854775809", 1<<63 - 1, strconv.ErrRange}, + {"-9223372036854775809", -1 << 63, strconv.ErrRange}, + {"-1_2_3_4_5", 0, strconv.ErrSyntax}, // base=10 so no underscores allowed + {"-_12345", 0, strconv.ErrSyntax}, + {"_12345", 0, strconv.ErrSyntax}, + {"1__2345", 0, strconv.ErrSyntax}, + {"12345_", 0, strconv.ErrSyntax}, + {"123%45", 0, strconv.ErrSyntax}, } type parseInt64BaseTest struct { @@ -173,7 +174,7 @@ type parseInt64BaseTest struct { } var parseInt64BaseTests = []parseInt64BaseTest{ - {"", 0, 0, ErrSyntax}, + {"", 0, 0, strconv.ErrSyntax}, {"0", 0, 0, nil}, {"-0", 0, 0, nil}, {"1", 0, 1, nil}, @@ -184,16 +185,16 @@ var parseInt64BaseTests = []parseInt64BaseTest{ {"-012345", 0, -012345, nil}, {"0x12345", 0, 0x12345, nil}, {"-0X12345", 0, -0x12345, nil}, - {"12345x", 0, 0, ErrSyntax}, - {"-12345x", 0, 0, ErrSyntax}, + {"12345x", 0, 0, strconv.ErrSyntax}, + {"-12345x", 0, 0, strconv.ErrSyntax}, {"98765432100", 0, 98765432100, nil}, {"-98765432100", 0, -98765432100, nil}, {"9223372036854775807", 0, 1<<63 - 1, nil}, {"-9223372036854775807", 0, -(1<<63 - 1), nil}, - {"9223372036854775808", 0, 1<<63 - 1, ErrRange}, + {"9223372036854775808", 0, 1<<63 - 1, strconv.ErrRange}, {"-9223372036854775808", 0, -1 << 63, nil}, - {"9223372036854775809", 0, 1<<63 - 1, ErrRange}, - {"-9223372036854775809", 0, -1 << 63, ErrRange}, + {"9223372036854775809", 0, 1<<63 - 1, strconv.ErrRange}, + {"-9223372036854775809", 0, -1 << 63, strconv.ErrRange}, // other bases {"g", 17, 16, nil}, @@ -207,9 +208,9 @@ var parseInt64BaseTests = []parseInt64BaseTest{ {"1010", 2, 10, nil}, {"1000000000000000", 2, 1 << 15, nil}, {"111111111111111111111111111111111111111111111111111111111111111", 2, 1<<63 - 1, nil}, - {"1000000000000000000000000000000000000000000000000000000000000000", 2, 1<<63 - 1, ErrRange}, + {"1000000000000000000000000000000000000000000000000000000000000000", 2, 1<<63 - 1, strconv.ErrRange}, {"-1000000000000000000000000000000000000000000000000000000000000000", 2, -1 << 63, nil}, - {"-1000000000000000000000000000000000000000000000000000000000000001", 2, -1 << 63, ErrRange}, + {"-1000000000000000000000000000000000000000000000000000000000000001", 2, -1 << 63, strconv.ErrRange}, // base 8 {"-10", 8, -8, nil}, @@ -224,27 +225,27 @@ var parseInt64BaseTests = []parseInt64BaseTest{ // underscores {"-0x_1_2_3_4_5", 0, -0x12345, nil}, {"0x_1_2_3_4_5", 0, 0x12345, nil}, - {"-_0x12345", 0, 0, ErrSyntax}, - {"_-0x12345", 0, 0, ErrSyntax}, - {"_0x12345", 0, 0, ErrSyntax}, - {"0x__12345", 0, 0, ErrSyntax}, - {"0x1__2345", 0, 0, ErrSyntax}, - {"0x1234__5", 0, 0, ErrSyntax}, - {"0x12345_", 0, 0, ErrSyntax}, + {"-_0x12345", 0, 0, strconv.ErrSyntax}, + {"_-0x12345", 0, 0, strconv.ErrSyntax}, + {"_0x12345", 0, 0, strconv.ErrSyntax}, + {"0x__12345", 0, 0, strconv.ErrSyntax}, + {"0x1__2345", 0, 0, strconv.ErrSyntax}, + {"0x1234__5", 0, 0, strconv.ErrSyntax}, + {"0x12345_", 0, 0, strconv.ErrSyntax}, {"-0_1_2_3_4_5", 0, -012345, nil}, // octal {"0_1_2_3_4_5", 0, 012345, nil}, // octal - {"-_012345", 0, 0, ErrSyntax}, - {"_-012345", 0, 0, ErrSyntax}, - {"_012345", 0, 0, ErrSyntax}, - {"0__12345", 0, 0, ErrSyntax}, - {"01234__5", 0, 0, ErrSyntax}, - {"012345_", 0, 0, ErrSyntax}, + {"-_012345", 0, 0, strconv.ErrSyntax}, + {"_-012345", 0, 0, strconv.ErrSyntax}, + {"_012345", 0, 0, strconv.ErrSyntax}, + {"0__12345", 0, 0, strconv.ErrSyntax}, + {"01234__5", 0, 0, strconv.ErrSyntax}, + {"012345_", 0, 0, strconv.ErrSyntax}, {"+0xf", 0, 0xf, nil}, {"-0xf", 0, -0xf, nil}, - {"0x+f", 0, 0, ErrSyntax}, - {"0x-f", 0, 0, ErrSyntax}, + {"0x+f", 0, 0, strconv.ErrSyntax}, + {"0x-f", 0, 0, strconv.ErrSyntax}, } type parseUint32Test struct { @@ -254,20 +255,20 @@ type parseUint32Test struct { } var parseUint32Tests = []parseUint32Test{ - {"", 0, ErrSyntax}, + {"", 0, strconv.ErrSyntax}, {"0", 0, nil}, {"1", 1, nil}, {"12345", 12345, nil}, {"012345", 12345, nil}, - {"12345x", 0, ErrSyntax}, + {"12345x", 0, strconv.ErrSyntax}, {"987654321", 987654321, nil}, {"4294967295", 1<<32 - 1, nil}, - {"4294967296", 1<<32 - 1, ErrRange}, - {"1_2_3_4_5", 0, ErrSyntax}, // base=10 so no underscores allowed - {"_12345", 0, ErrSyntax}, - {"_12345", 0, ErrSyntax}, - {"1__2345", 0, ErrSyntax}, - {"12345_", 0, ErrSyntax}, + {"4294967296", 1<<32 - 1, strconv.ErrRange}, + {"1_2_3_4_5", 0, strconv.ErrSyntax}, // base=10 so no underscores allowed + {"_12345", 0, strconv.ErrSyntax}, + {"_12345", 0, strconv.ErrSyntax}, + {"1__2345", 0, strconv.ErrSyntax}, + {"12345_", 0, strconv.ErrSyntax}, } type parseInt32Test struct { @@ -277,7 +278,7 @@ type parseInt32Test struct { } var parseInt32Tests = []parseInt32Test{ - {"", 0, ErrSyntax}, + {"", 0, strconv.ErrSyntax}, {"0", 0, nil}, {"-0", 0, nil}, {"1", 1, nil}, @@ -286,22 +287,22 @@ var parseInt32Tests = []parseInt32Test{ {"-12345", -12345, nil}, {"012345", 12345, nil}, {"-012345", -12345, nil}, - {"12345x", 0, ErrSyntax}, - {"-12345x", 0, ErrSyntax}, + {"12345x", 0, strconv.ErrSyntax}, + {"-12345x", 0, strconv.ErrSyntax}, {"987654321", 987654321, nil}, {"-987654321", -987654321, nil}, {"2147483647", 1<<31 - 1, nil}, {"-2147483647", -(1<<31 - 1), nil}, - {"2147483648", 1<<31 - 1, ErrRange}, + {"2147483648", 1<<31 - 1, strconv.ErrRange}, {"-2147483648", -1 << 31, nil}, - {"2147483649", 1<<31 - 1, ErrRange}, - {"-2147483649", -1 << 31, ErrRange}, - {"-1_2_3_4_5", 0, ErrSyntax}, // base=10 so no underscores allowed - {"-_12345", 0, ErrSyntax}, - {"_12345", 0, ErrSyntax}, - {"1__2345", 0, ErrSyntax}, - {"12345_", 0, ErrSyntax}, - {"123%45", 0, ErrSyntax}, + {"2147483649", 1<<31 - 1, strconv.ErrRange}, + {"-2147483649", -1 << 31, strconv.ErrRange}, + {"-1_2_3_4_5", 0, strconv.ErrSyntax}, // base=10 so no underscores allowed + {"-_12345", 0, strconv.ErrSyntax}, + {"_12345", 0, strconv.ErrSyntax}, + {"1__2345", 0, strconv.ErrSyntax}, + {"12345_", 0, strconv.ErrSyntax}, + {"123%45", 0, strconv.ErrSyntax}, } type numErrorTest struct { @@ -320,37 +321,37 @@ func init() { for i := range parseUint64Tests { test := &parseUint64Tests[i] if test.err != nil { - test.err = &NumError{"ParseUint", test.in, test.err} + test.err = &strconv.NumError{"ParseUint", test.in, test.err} } } for i := range parseUint64BaseTests { test := &parseUint64BaseTests[i] if test.err != nil { - test.err = &NumError{"ParseUint", test.in, test.err} + test.err = &strconv.NumError{"ParseUint", test.in, test.err} } } for i := range parseInt64Tests { test := &parseInt64Tests[i] if test.err != nil { - test.err = &NumError{"ParseInt", test.in, test.err} + test.err = &strconv.NumError{"ParseInt", test.in, test.err} } } for i := range parseInt64BaseTests { test := &parseInt64BaseTests[i] if test.err != nil { - test.err = &NumError{"ParseInt", test.in, test.err} + test.err = &strconv.NumError{"ParseInt", test.in, test.err} } } for i := range parseUint32Tests { test := &parseUint32Tests[i] if test.err != nil { - test.err = &NumError{"ParseUint", test.in, test.err} + test.err = &strconv.NumError{"ParseUint", test.in, test.err} } } for i := range parseInt32Tests { test := &parseInt32Tests[i] if test.err != nil { - test.err = &NumError{"ParseInt", test.in, test.err} + test.err = &strconv.NumError{"ParseInt", test.in, test.err} } } } @@ -358,7 +359,7 @@ func init() { func TestParseUint32(t *testing.T) { for i := range parseUint32Tests { test := &parseUint32Tests[i] - out, err := ParseUint(test.in, 10, 32) + out, err := strconv.ParseUint(test.in, 10, 32) if uint64(test.out) != out || !errEqual(test.err, err) { t.Errorf("ParseUint(%q, 10, 32) = %v, %v want %v, %v", test.in, out, err, test.out, test.err) @@ -369,7 +370,7 @@ func TestParseUint32(t *testing.T) { func TestParseUint64(t *testing.T) { for i := range parseUint64Tests { test := &parseUint64Tests[i] - out, err := ParseUint(test.in, 10, 64) + out, err := strconv.ParseUint(test.in, 10, 64) if test.out != out || !errEqual(test.err, err) { t.Errorf("ParseUint(%q, 10, 64) = %v, %v want %v, %v", test.in, out, err, test.out, test.err) @@ -380,7 +381,7 @@ func TestParseUint64(t *testing.T) { func TestParseUint64Base(t *testing.T) { for i := range parseUint64BaseTests { test := &parseUint64BaseTests[i] - out, err := ParseUint(test.in, test.base, 64) + out, err := strconv.ParseUint(test.in, test.base, 64) if test.out != out || !errEqual(test.err, err) { t.Errorf("ParseUint(%q, %v, 64) = %v, %v want %v, %v", test.in, test.base, out, err, test.out, test.err) @@ -391,7 +392,7 @@ func TestParseUint64Base(t *testing.T) { func TestParseInt32(t *testing.T) { for i := range parseInt32Tests { test := &parseInt32Tests[i] - out, err := ParseInt(test.in, 10, 32) + out, err := strconv.ParseInt(test.in, 10, 32) if int64(test.out) != out || !errEqual(test.err, err) { t.Errorf("ParseInt(%q, 10 ,32) = %v, %v want %v, %v", test.in, out, err, test.out, test.err) @@ -402,7 +403,7 @@ func TestParseInt32(t *testing.T) { func TestParseInt64(t *testing.T) { for i := range parseInt64Tests { test := &parseInt64Tests[i] - out, err := ParseInt(test.in, 10, 64) + out, err := strconv.ParseInt(test.in, 10, 64) if test.out != out || !errEqual(test.err, err) { t.Errorf("ParseInt(%q, 10, 64) = %v, %v want %v, %v", test.in, out, err, test.out, test.err) @@ -413,7 +414,7 @@ func TestParseInt64(t *testing.T) { func TestParseInt64Base(t *testing.T) { for i := range parseInt64BaseTests { test := &parseInt64BaseTests[i] - out, err := ParseInt(test.in, test.base, 64) + out, err := strconv.ParseInt(test.in, test.base, 64) if test.out != out || !errEqual(test.err, err) { t.Errorf("ParseInt(%q, %v, 64) = %v, %v want %v, %v", test.in, test.base, out, err, test.out, test.err) @@ -422,11 +423,11 @@ func TestParseInt64Base(t *testing.T) { } func TestParseUint(t *testing.T) { - switch IntSize { + switch strconv.IntSize { case 32: for i := range parseUint32Tests { test := &parseUint32Tests[i] - out, err := ParseUint(test.in, 10, 0) + out, err := strconv.ParseUint(test.in, 10, 0) if uint64(test.out) != out || !errEqual(test.err, err) { t.Errorf("ParseUint(%q, 10, 0) = %v, %v want %v, %v", test.in, out, err, test.out, test.err) @@ -435,7 +436,7 @@ func TestParseUint(t *testing.T) { case 64: for i := range parseUint64Tests { test := &parseUint64Tests[i] - out, err := ParseUint(test.in, 10, 0) + out, err := strconv.ParseUint(test.in, 10, 0) if test.out != out || !errEqual(test.err, err) { t.Errorf("ParseUint(%q, 10, 0) = %v, %v want %v, %v", test.in, out, err, test.out, test.err) @@ -445,11 +446,11 @@ func TestParseUint(t *testing.T) { } func TestParseInt(t *testing.T) { - switch IntSize { + switch strconv.IntSize { case 32: for i := range parseInt32Tests { test := &parseInt32Tests[i] - out, err := ParseInt(test.in, 10, 0) + out, err := strconv.ParseInt(test.in, 10, 0) if int64(test.out) != out || !errEqual(test.err, err) { t.Errorf("ParseInt(%q, 10, 0) = %v, %v want %v, %v", test.in, out, err, test.out, test.err) @@ -458,7 +459,7 @@ func TestParseInt(t *testing.T) { case 64: for i := range parseInt64Tests { test := &parseInt64Tests[i] - out, err := ParseInt(test.in, 10, 0) + out, err := strconv.ParseInt(test.in, 10, 0) if test.out != out || !errEqual(test.err, err) { t.Errorf("ParseInt(%q, 10, 0) = %v, %v want %v, %v", test.in, out, err, test.out, test.err) @@ -468,14 +469,14 @@ func TestParseInt(t *testing.T) { } func TestAtoi(t *testing.T) { - switch IntSize { + switch strconv.IntSize { case 32: for i := range parseInt32Tests { test := &parseInt32Tests[i] - out, err := Atoi(test.in) + out, err := strconv.Atoi(test.in) var testErr error if test.err != nil { - testErr = &NumError{"Atoi", test.in, test.err.(*NumError).Err} + testErr = &strconv.NumError{"Atoi", test.in, test.err.(*strconv.NumError).Err} } if int(test.out) != out || !errEqual(testErr, err) { t.Errorf("Atoi(%q) = %v, %v want %v, %v", @@ -485,10 +486,10 @@ func TestAtoi(t *testing.T) { case 64: for i := range parseInt64Tests { test := &parseInt64Tests[i] - out, err := Atoi(test.in) + out, err := strconv.Atoi(test.in) var testErr error if test.err != nil { - testErr = &NumError{"Atoi", test.in, test.err.(*NumError).Err} + testErr = &strconv.NumError{"Atoi", test.in, test.err.(*strconv.NumError).Err} } if test.out != int64(out) || !errEqual(testErr, err) { t.Errorf("Atoi(%q) = %v, %v want %v, %v", @@ -499,11 +500,11 @@ func TestAtoi(t *testing.T) { } func bitSizeErrStub(name string, bitSize int) error { - return BitSizeError(name, "0", bitSize) + return strconv.BitSizeError(name, "0", bitSize) } func baseErrStub(name string, base int) error { - return BaseError(name, "0", base) + return strconv.BaseError(name, "0", base) } func noErrStub(name string, arg int) error { @@ -545,7 +546,7 @@ func TestParseIntBitSize(t *testing.T) { for i := range parseBitSizeTests { test := &parseBitSizeTests[i] testErr := test.errStub("ParseInt", test.arg) - _, err := ParseInt("0", 0, test.arg) + _, err := strconv.ParseInt("0", 0, test.arg) if !equalError(testErr, err) { t.Errorf("ParseInt(\"0\", 0, %v) = 0, %v want 0, %v", test.arg, err, testErr) @@ -557,7 +558,7 @@ func TestParseUintBitSize(t *testing.T) { for i := range parseBitSizeTests { test := &parseBitSizeTests[i] testErr := test.errStub("ParseUint", test.arg) - _, err := ParseUint("0", 0, test.arg) + _, err := strconv.ParseUint("0", 0, test.arg) if !equalError(testErr, err) { t.Errorf("ParseUint(\"0\", 0, %v) = 0, %v want 0, %v", test.arg, err, testErr) @@ -569,7 +570,7 @@ func TestParseIntBase(t *testing.T) { for i := range parseBaseTests { test := &parseBaseTests[i] testErr := test.errStub("ParseInt", test.arg) - _, err := ParseInt("0", test.arg, 0) + _, err := strconv.ParseInt("0", test.arg, 0) if !equalError(testErr, err) { t.Errorf("ParseInt(\"0\", %v, 0) = 0, %v want 0, %v", test.arg, err, testErr) @@ -581,7 +582,7 @@ func TestParseUintBase(t *testing.T) { for i := range parseBaseTests { test := &parseBaseTests[i] testErr := test.errStub("ParseUint", test.arg) - _, err := ParseUint("0", test.arg, 0) + _, err := strconv.ParseUint("0", test.arg, 0) if !equalError(testErr, err) { t.Errorf("ParseUint(\"0\", %v, 0) = 0, %v want 0, %v", test.arg, err, testErr) @@ -591,21 +592,21 @@ func TestParseUintBase(t *testing.T) { func TestNumError(t *testing.T) { for _, test := range numErrorTests { - err := &NumError{ + err := &strconv.NumError{ Func: "ParseFloat", Num: test.num, Err: errors.New("failed"), } if got := err.Error(); got != test.want { - t.Errorf(`(&NumError{"ParseFloat", %q, "failed"}).Error() = %v, want %v`, test.num, got, test.want) + t.Errorf(`(&strconv.NumError{"ParseFloat", %q, "failed"}).Error() = %v, want %v`, test.num, got, test.want) } } } /* XXX: add when we support reflection / error un/wrapping. func TestNumErrorUnwrap(t *testing.T) { - err := &NumError{Err: ErrSyntax} - if !errEqual(err, ErrSyntax) { + err := &strconv.NumError{Err: strconv.ErrSyntax} + if !errEqual(err, strconv.ErrSyntax) { t.Error("errors.Is failed, wanted success") } } @@ -637,7 +638,7 @@ func benchmarkParseInt(b *testing.B, neg int) { b.Run(cs.name, func(b *testing.B) { s := fmt.Sprintf("%d", cs.num*int64(neg)) for i := 0; i < b.N; i++ { - out, _ := ParseInt(s, 10, 64) + out, _ := strconv.ParseInt(s, 10, 64) BenchSink += int(out) } }) @@ -659,7 +660,7 @@ func benchmarkAtoi(b *testing.B, neg int) { {"26bit", 1<<26 - 1}, {"31bit", 1<<31 - 1}, } - if IntSize == 64 { + if strconv.IntSize == 64 { cases = append(cases, []benchCase{ {"56bit", 1<<56 - 1}, {"63bit", 1<<63 - 1}, @@ -669,7 +670,7 @@ func benchmarkAtoi(b *testing.B, neg int) { b.Run(cs.name, func(b *testing.B) { s := fmt.Sprintf("%d", cs.num*int64(neg)) for i := 0; i < b.N; i++ { - out, _ := Atoi(s) + out, _ := strconv.Atoi(s) BenchSink += out } }) diff --git a/gnovm/stdlibs/strconv/decimal_test.gno b/gnovm/stdlibs/strconv/decimal_test.gno index 9dc8c997b9c..58036fe69b0 100644 --- a/gnovm/stdlibs/strconv/decimal_test.gno +++ b/gnovm/stdlibs/strconv/decimal_test.gno @@ -2,9 +2,10 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package strconv +package strconv_test import ( + "strconv" "testing" ) @@ -31,7 +32,7 @@ var shifttests = []shiftTest{ func TestDecimalShift(t *testing.T) { for i := 0; i < len(shifttests); i++ { test := &shifttests[i] - d := NewDecimal(test.i) + d := strconv.NewDecimal(test.i) d.Shift(test.shift) s := d.String() if s != test.out { @@ -69,21 +70,21 @@ var roundtests = []roundTest{ func TestDecimalRound(t *testing.T) { for i := 0; i < len(roundtests); i++ { test := &roundtests[i] - d := NewDecimal(test.i) + d := strconv.NewDecimal(test.i) d.RoundDown(test.nd) s := d.String() if s != test.down { t.Errorf("Decimal %v RoundDown %d = %v, want %v", test.i, test.nd, s, test.down) } - d = NewDecimal(test.i) + d = strconv.NewDecimal(test.i) d.Round(test.nd) s = d.String() if s != test.round { t.Errorf("Decimal %v Round %d = %v, want %v", test.i, test.nd, s, test.down) } - d = NewDecimal(test.i) + d = strconv.NewDecimal(test.i) d.RoundUp(test.nd) s = d.String() if s != test.up { @@ -115,7 +116,7 @@ var roundinttests = []roundIntTest{ func TestDecimalRoundedInteger(t *testing.T) { for i := 0; i < len(roundinttests); i++ { test := roundinttests[i] - d := NewDecimal(test.i) + d := strconv.NewDecimal(test.i) d.Shift(test.shift) num := d.RoundedInteger() if num != test.int { diff --git a/gnovm/stdlibs/strconv/ftoa_test.gno b/gnovm/stdlibs/strconv/ftoa_test.gno index df1cc733827..bb860d62278 100644 --- a/gnovm/stdlibs/strconv/ftoa_test.gno +++ b/gnovm/stdlibs/strconv/ftoa_test.gno @@ -2,11 +2,12 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package strconv +package strconv_test import ( "math" "math/rand" + "strconv" "testing" ) @@ -176,20 +177,20 @@ var ftoatests = []ftoaTest{ func TestFtoa(t *testing.T) { for i := 0; i < len(ftoatests); i++ { test := &ftoatests[i] - s := FormatFloat(test.f, test.fmt, test.prec, 64) + s := strconv.FormatFloat(test.f, test.fmt, test.prec, 64) if s != test.s { t.Error("testN=64", test.f, string(test.fmt), test.prec, "want", test.s, "got", s) } - x := AppendFloat([]byte("abc"), test.f, test.fmt, test.prec, 64) + x := strconv.AppendFloat([]byte("abc"), test.f, test.fmt, test.prec, 64) if string(x) != "abc"+test.s { t.Error("AppendFloat testN=64", test.f, string(test.fmt), test.prec, "want", "abc"+test.s, "got", string(x)) } if float64(float32(test.f)) == test.f && test.fmt != 'b' { - s := FormatFloat(test.f, test.fmt, test.prec, 32) + s := strconv.FormatFloat(test.f, test.fmt, test.prec, 32) if s != test.s { t.Error("testN=32", test.f, string(test.fmt), test.prec, "want", test.s, "got", s) } - x := AppendFloat([]byte("abc"), test.f, test.fmt, test.prec, 32) + x := strconv.AppendFloat([]byte("abc"), test.f, test.fmt, test.prec, 32) if string(x) != "abc"+test.s { t.Error("AppendFloat testN=32", test.f, string(test.fmt), test.prec, "want", "abc"+test.s, "got", string(x)) } @@ -201,15 +202,15 @@ func TestFtoaPowersOfTwo(t *testing.T) { for exp := -2048; exp <= 2048; exp++ { f := math.Ldexp(1, exp) if !math.IsInf(f, 0) { - s := FormatFloat(f, 'e', -1, 64) - if x, _ := ParseFloat(s, 64); x != f { + s := strconv.FormatFloat(f, 'e', -1, 64) + if x, _ := strconv.ParseFloat(s, 64); x != f { t.Errorf("failed roundtrip %v => %s => %v", f, s, x) } } f32 := float32(f) if !math.IsInf(float64(f32), 0) { - s := FormatFloat(float64(f32), 'e', -1, 32) - if x, _ := ParseFloat(s, 32); float32(x) != f32 { + s := strconv.FormatFloat(float64(f32), 'e', -1, 32) + if x, _ := strconv.ParseFloat(s, 32); float32(x) != f32 { t.Errorf("failed roundtrip %v => %s => %v", f32, s, float32(x)) } } @@ -226,19 +227,19 @@ func TestFtoaRandom(t *testing.T) { bits := uint64(rand.Uint32())<<32 | uint64(rand.Uint32()) x := math.Float64frombits(bits) - shortFast := FormatFloat(x, 'g', -1, 64) - SetOptimize(false) - shortSlow := FormatFloat(x, 'g', -1, 64) - SetOptimize(true) + shortFast := strconv.FormatFloat(x, 'g', -1, 64) + strconv.SetOptimize(false) + shortSlow := strconv.FormatFloat(x, 'g', -1, 64) + strconv.SetOptimize(true) if shortSlow != shortFast { t.Errorf("%b printed as %s, want %s", x, shortFast, shortSlow) } prec := rand.IntN(12) + 5 - shortFast = FormatFloat(x, 'e', prec, 64) - SetOptimize(false) - shortSlow = FormatFloat(x, 'e', prec, 64) - SetOptimize(true) + shortFast = strconv.FormatFloat(x, 'e', prec, 64) + strconv.SetOptimize(false) + shortSlow = strconv.FormatFloat(x, 'e', prec, 64) + strconv.SetOptimize(true) if shortSlow != shortFast { t.Errorf("%b printed as %s, want %s", x, shortFast, shortSlow) } @@ -251,7 +252,7 @@ func TestFormatFloatInvalidBitSize(t *testing.T) { t.Fatalf("expected panic due to invalid bitSize") } }() - _ = FormatFloat(3.14, 'g', -1, 100) + _ = strconv.FormatFloat(3.14, 'g', -1, 100) } var ftoaBenches = []struct { @@ -305,7 +306,7 @@ func BenchmarkFormatFloat(b *testing.B) { for _, c := range ftoaBenches { b.Run(c.name, func(b *testing.B) { for i := 0; i < b.N; i++ { - FormatFloat(c.float, c.fmt, c.prec, c.bitSize) + strconv.FormatFloat(c.float, c.fmt, c.prec, c.bitSize) } }) } @@ -316,7 +317,7 @@ func BenchmarkAppendFloat(b *testing.B) { for _, c := range ftoaBenches { b.Run(c.name, func(b *testing.B) { for i := 0; i < b.N; i++ { - AppendFloat(dst[:0], c.float, c.fmt, c.prec, c.bitSize) + strconv.AppendFloat(dst[:0], c.float, c.fmt, c.prec, c.bitSize) } }) } diff --git a/gnovm/stdlibs/strconv/ftoaryu_test.gno b/gnovm/stdlibs/strconv/ftoaryu_test.gno index bd969e8e997..8a7cb1fb97d 100644 --- a/gnovm/stdlibs/strconv/ftoaryu_test.gno +++ b/gnovm/stdlibs/strconv/ftoaryu_test.gno @@ -2,16 +2,17 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package strconv +package strconv_test import ( "math" + "strconv" "testing" ) func TestMulByLog2Log10(t *testing.T) { for x := -1600; x <= +1600; x++ { - iMath := MulByLog2Log10(x) + iMath := strconv.MulByLog2Log10(x) fMath := int(math.Floor(float64(x) * math.Ln2 / math.Ln10)) if iMath != fMath { t.Errorf("mulByLog2Log10(%d) failed: %d vs %d\n", x, iMath, fMath) @@ -21,7 +22,7 @@ func TestMulByLog2Log10(t *testing.T) { func TestMulByLog10Log2(t *testing.T) { for x := -500; x <= +500; x++ { - iMath := MulByLog10Log2(x) + iMath := strconv.MulByLog10Log2(x) fMath := int(math.Floor(float64(x) * math.Ln10 / math.Ln2)) if iMath != fMath { t.Errorf("mulByLog10Log2(%d) failed: %d vs %d\n", x, iMath, fMath) diff --git a/gnovm/stdlibs/strconv/itoa_test.gno b/gnovm/stdlibs/strconv/itoa_test.gno index b76acc78183..e463edeb5eb 100644 --- a/gnovm/stdlibs/strconv/itoa_test.gno +++ b/gnovm/stdlibs/strconv/itoa_test.gno @@ -2,9 +2,10 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package strconv +package strconv_test import ( + "strconv" "testing" ) @@ -60,24 +61,24 @@ var itob64tests = []itob64Test{ func TestItoa(t *testing.T) { for _, test := range itob64tests { - s := FormatInt(test.in, test.base) + s := strconv.FormatInt(test.in, test.base) if s != test.out { t.Errorf("FormatInt(%v, %v) = %v want %v", test.in, test.base, s, test.out) } - x := AppendInt([]byte("abc"), test.in, test.base) + x := strconv.AppendInt([]byte("abc"), test.in, test.base) if string(x) != "abc"+test.out { t.Errorf("AppendInt(%q, %v, %v) = %q want %v", "abc", test.in, test.base, x, test.out) } if test.in >= 0 { - s := FormatUint(uint64(test.in), test.base) + s := strconv.FormatUint(uint64(test.in), test.base) if s != test.out { t.Errorf("FormatUint(%v, %v) = %v want %v", test.in, test.base, s, test.out) } - x := AppendUint(nil, uint64(test.in), test.base) + x := strconv.AppendUint(nil, uint64(test.in), test.base) if string(x) != test.out { t.Errorf("AppendUint(%q, %v, %v) = %q want %v", "abc", uint64(test.in), test.base, x, test.out) @@ -85,7 +86,7 @@ func TestItoa(t *testing.T) { } if test.base == 10 && int64(int(test.in)) == test.in { - s := Itoa(int(test.in)) + s := strconv.Itoa(int(test.in)) if s != test.out { t.Errorf("Itoa(%v) = %v want %v", test.in, s, test.out) @@ -99,7 +100,7 @@ func TestItoa(t *testing.T) { t.Fatalf("expected panic due to illegal base") } }() - FormatUint(12345678, 1) + strconv.FormatUint(12345678, 1) } type uitob64Test struct { @@ -119,12 +120,12 @@ var uitob64tests = []uitob64Test{ func TestUitoa(t *testing.T) { for _, test := range uitob64tests { - s := FormatUint(test.in, test.base) + s := strconv.FormatUint(test.in, test.base) if s != test.out { t.Errorf("FormatUint(%v, %v) = %v want %v", test.in, test.base, s, test.out) } - x := AppendUint([]byte("abc"), test.in, test.base) + x := strconv.AppendUint([]byte("abc"), test.in, test.base) if string(x) != "abc"+test.out { t.Errorf("AppendUint(%q, %v, %v) = %q want %v", "abc", test.in, test.base, x, test.out) @@ -161,7 +162,7 @@ var varlenUints = []struct { func TestFormatUintVarlen(t *testing.T) { for _, test := range varlenUints { - s := FormatUint(test.in, 10) + s := strconv.FormatUint(test.in, 10) if s != test.out { t.Errorf("FormatUint(%v, 10) = %v want %v", test.in, s, test.out) } @@ -171,7 +172,7 @@ func TestFormatUintVarlen(t *testing.T) { func BenchmarkFormatInt(b *testing.B) { for i := 0; i < b.N; i++ { for _, test := range itob64tests { - s := FormatInt(test.in, test.base) + s := strconv.FormatInt(test.in, test.base) BenchSink += len(s) } } @@ -181,7 +182,7 @@ func BenchmarkAppendInt(b *testing.B) { dst := make([]byte, 0, 30) for i := 0; i < b.N; i++ { for _, test := range itob64tests { - dst = AppendInt(dst[:0], test.in, test.base) + dst = strconv.AppendInt(dst[:0], test.in, test.base) BenchSink += len(dst) } } @@ -190,7 +191,7 @@ func BenchmarkAppendInt(b *testing.B) { func BenchmarkFormatUint(b *testing.B) { for i := 0; i < b.N; i++ { for _, test := range uitob64tests { - s := FormatUint(test.in, test.base) + s := strconv.FormatUint(test.in, test.base) BenchSink += len(s) } } @@ -200,7 +201,7 @@ func BenchmarkAppendUint(b *testing.B) { dst := make([]byte, 0, 30) for i := 0; i < b.N; i++ { for _, test := range uitob64tests { - dst = AppendUint(dst[:0], test.in, test.base) + dst = strconv.AppendUint(dst[:0], test.in, test.base) BenchSink += len(dst) } } @@ -209,9 +210,9 @@ func BenchmarkAppendUint(b *testing.B) { func BenchmarkFormatIntSmall(b *testing.B) { smallInts := []int64{7, 42} for _, smallInt := range smallInts { - b.Run(Itoa(int(smallInt)), func(b *testing.B) { + b.Run(strconv.Itoa(int(smallInt)), func(b *testing.B) { for i := 0; i < b.N; i++ { - s := FormatInt(smallInt, 10) + s := strconv.FormatInt(smallInt, 10) BenchSink += len(s) } }) @@ -222,7 +223,7 @@ func BenchmarkAppendIntSmall(b *testing.B) { dst := make([]byte, 0, 30) const smallInt = 42 for i := 0; i < b.N; i++ { - dst = AppendInt(dst[:0], smallInt, 10) + dst = strconv.AppendInt(dst[:0], smallInt, 10) BenchSink += len(dst) } } @@ -232,7 +233,7 @@ func BenchmarkAppendUintVarlen(b *testing.B) { b.Run(test.out, func(b *testing.B) { dst := make([]byte, 0, 30) for j := 0; j < b.N; j++ { - dst = AppendUint(dst[:0], test.in, 10) + dst = strconv.AppendUint(dst[:0], test.in, 10) BenchSink += len(dst) } }) diff --git a/gnovm/stdlibs/strconv/quote_test.gno b/gnovm/stdlibs/strconv/quote_test.gno index b11e95461b0..40601365a30 100644 --- a/gnovm/stdlibs/strconv/quote_test.gno +++ b/gnovm/stdlibs/strconv/quote_test.gno @@ -2,9 +2,10 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package strconv +package strconv_test import ( + "strconv" "strings" "testing" "unicode" @@ -14,8 +15,8 @@ import ( func TestIsPrint(t *testing.T) { n := 0 for r := rune(0); r <= unicode.MaxRune; r++ { - if IsPrint(r) != unicode.IsPrint(r) { - t.Errorf("IsPrint(%U)=%t incorrect", r, IsPrint(r)) + if strconv.IsPrint(r) != unicode.IsPrint(r) { + t.Errorf("IsPrint(%U)=%t incorrect", r, strconv.IsPrint(r)) n++ if n > 10 { return @@ -28,8 +29,8 @@ func TestIsPrint(t *testing.T) { func TestIsGraphic(t *testing.T) { n := 0 for r := rune(0); r <= unicode.MaxRune; r++ { - if IsGraphic(r) != unicode.IsGraphic(r) { - t.Errorf("IsGraphic(%U)=%t incorrect", r, IsGraphic(r)) + if strconv.IsGraphic(r) != unicode.IsGraphic(r) { + t.Errorf("IsGraphic(%U)=%t incorrect", r, strconv.IsGraphic(r)) n++ if n > 10 { return @@ -59,10 +60,10 @@ var quotetests = []quoteTest{ func TestQuote(t *testing.T) { for _, tt := range quotetests { - if out := Quote(tt.in); out != tt.out { + if out := strconv.Quote(tt.in); out != tt.out { t.Errorf("Quote(%s) = %s, want %s", tt.in, out, tt.out) } - if out := AppendQuote([]byte("abc"), tt.in); string(out) != "abc"+tt.out { + if out := strconv.AppendQuote([]byte("abc"), tt.in); string(out) != "abc"+tt.out { t.Errorf("AppendQuote(%q, %s) = %s, want %s", "abc", tt.in, out, "abc"+tt.out) } } @@ -70,10 +71,10 @@ func TestQuote(t *testing.T) { func TestQuoteToASCII(t *testing.T) { for _, tt := range quotetests { - if out := QuoteToASCII(tt.in); out != tt.ascii { + if out := strconv.QuoteToASCII(tt.in); out != tt.ascii { t.Errorf("QuoteToASCII(%s) = %s, want %s", tt.in, out, tt.ascii) } - if out := AppendQuoteToASCII([]byte("abc"), tt.in); string(out) != "abc"+tt.ascii { + if out := strconv.AppendQuoteToASCII([]byte("abc"), tt.in); string(out) != "abc"+tt.ascii { t.Errorf("AppendQuoteToASCII(%q, %s) = %s, want %s", "abc", tt.in, out, "abc"+tt.ascii) } } @@ -81,10 +82,10 @@ func TestQuoteToASCII(t *testing.T) { func TestQuoteToGraphic(t *testing.T) { for _, tt := range quotetests { - if out := QuoteToGraphic(tt.in); out != tt.graphic { + if out := strconv.QuoteToGraphic(tt.in); out != tt.graphic { t.Errorf("QuoteToGraphic(%s) = %s, want %s", tt.in, out, tt.graphic) } - if out := AppendQuoteToGraphic([]byte("abc"), tt.in); string(out) != "abc"+tt.graphic { + if out := strconv.AppendQuoteToGraphic([]byte("abc"), tt.in); string(out) != "abc"+tt.graphic { t.Errorf("AppendQuoteToGraphic(%q, %s) = %s, want %s", "abc", tt.in, out, "abc"+tt.graphic) } } @@ -92,13 +93,13 @@ func TestQuoteToGraphic(t *testing.T) { func BenchmarkQuote(b *testing.B) { for i := 0; i < b.N; i++ { - Quote("\a\b\f\r\n\t\v\a\b\f\r\n\t\v\a\b\f\r\n\t\v") + strconv.Quote("\a\b\f\r\n\t\v\a\b\f\r\n\t\v\a\b\f\r\n\t\v") } } func BenchmarkQuoteRune(b *testing.B) { for i := 0; i < b.N; i++ { - QuoteRune('\a') + strconv.QuoteRune('\a') } } @@ -106,7 +107,7 @@ var benchQuoteBuf []byte func BenchmarkAppendQuote(b *testing.B) { for i := 0; i < b.N; i++ { - benchQuoteBuf = AppendQuote(benchQuoteBuf[:0], "\a\b\f\r\n\t\v\a\b\f\r\n\t\v\a\b\f\r\n\t\v") + benchQuoteBuf = strconv.AppendQuote(benchQuoteBuf[:0], "\a\b\f\r\n\t\v\a\b\f\r\n\t\v\a\b\f\r\n\t\v") } } @@ -114,7 +115,7 @@ var benchQuoteRuneBuf []byte func BenchmarkAppendQuoteRune(b *testing.B) { for i := 0; i < b.N; i++ { - benchQuoteRuneBuf = AppendQuoteRune(benchQuoteRuneBuf[:0], '\a') + benchQuoteRuneBuf = strconv.AppendQuoteRune(benchQuoteRuneBuf[:0], '\a') } } @@ -144,10 +145,10 @@ var quoterunetests = []quoteRuneTest{ func TestQuoteRune(t *testing.T) { for _, tt := range quoterunetests { - if out := QuoteRune(tt.in); out != tt.out { + if out := strconv.QuoteRune(tt.in); out != tt.out { t.Errorf("QuoteRune(%U) = %s, want %s", tt.in, out, tt.out) } - if out := AppendQuoteRune([]byte("abc"), tt.in); string(out) != "abc"+tt.out { + if out := strconv.AppendQuoteRune([]byte("abc"), tt.in); string(out) != "abc"+tt.out { t.Errorf("AppendQuoteRune(%q, %U) = %s, want %s", "abc", tt.in, out, "abc"+tt.out) } } @@ -155,10 +156,10 @@ func TestQuoteRune(t *testing.T) { func TestQuoteRuneToASCII(t *testing.T) { for _, tt := range quoterunetests { - if out := QuoteRuneToASCII(tt.in); out != tt.ascii { + if out := strconv.QuoteRuneToASCII(tt.in); out != tt.ascii { t.Errorf("QuoteRuneToASCII(%U) = %s, want %s", tt.in, out, tt.ascii) } - if out := AppendQuoteRuneToASCII([]byte("abc"), tt.in); string(out) != "abc"+tt.ascii { + if out := strconv.AppendQuoteRuneToASCII([]byte("abc"), tt.in); string(out) != "abc"+tt.ascii { t.Errorf("AppendQuoteRuneToASCII(%q, %U) = %s, want %s", "abc", tt.in, out, "abc"+tt.ascii) } } @@ -166,10 +167,10 @@ func TestQuoteRuneToASCII(t *testing.T) { func TestQuoteRuneToGraphic(t *testing.T) { for _, tt := range quoterunetests { - if out := QuoteRuneToGraphic(tt.in); out != tt.graphic { + if out := strconv.QuoteRuneToGraphic(tt.in); out != tt.graphic { t.Errorf("QuoteRuneToGraphic(%U) = %s, want %s", tt.in, out, tt.graphic) } - if out := AppendQuoteRuneToGraphic([]byte("abc"), tt.in); string(out) != "abc"+tt.graphic { + if out := strconv.AppendQuoteRuneToGraphic([]byte("abc"), tt.in); string(out) != "abc"+tt.graphic { t.Errorf("AppendQuoteRuneToGraphic(%q, %U) = %s, want %s", "abc", tt.in, out, "abc"+tt.graphic) } } @@ -228,7 +229,7 @@ var canbackquotetests = []canBackquoteTest{ func TestCanBackquote(t *testing.T) { for _, tt := range canbackquotetests { - if out := CanBackquote(tt.in); out != tt.out { + if out := strconv.CanBackquote(tt.in); out != tt.out { t.Errorf("CanBackquote(%q) = %v, want %v", tt.in, out, tt.out) } } @@ -318,7 +319,7 @@ func TestUnquote(t *testing.T) { testUnquote(t, tt.out, tt.in, nil) } for _, s := range misquoted { - testUnquote(t, s, "", ErrSyntax) + testUnquote(t, s, "", strconv.ErrSyntax) } } @@ -332,7 +333,7 @@ func TestUnquoteInvalidUTF8(t *testing.T) { wantErr error }{ {in: `"foo"`, want: "foo"}, - {in: `"foo`, wantErr: ErrSyntax}, + {in: `"foo`, wantErr: strconv.ErrSyntax}, {in: `"` + "\xc0" + `"`, want: "\xef\xbf\xbd"}, {in: `"a` + "\xc0" + `"`, want: "a\xef\xbf\xbd"}, {in: `"\t` + "\xc0" + `"`, want: "\t\xef\xbf\xbd"}, @@ -344,7 +345,7 @@ func TestUnquoteInvalidUTF8(t *testing.T) { func testUnquote(t *testing.T, in, want string, wantErr error) { // Test Unquote. - got, gotErr := Unquote(in) + got, gotErr := strconv.Unquote(in) if got != want || gotErr != wantErr { t.Errorf("Unquote(%q) = (%q, %v), want (%q, %v)", in, got, gotErr, want, wantErr) } @@ -360,9 +361,9 @@ func testUnquote(t *testing.T, in, want string, wantErr error) { suffix = strings.ReplaceAll(suffix, in[:1], "") } in += suffix - got, gotErr = QuotedPrefix(in) + got, gotErr = strconv.QuotedPrefix(in) if gotErr == nil && wantErr != nil { - _, wantErr = Unquote(got) // original input had trailing junk, reparse with only valid prefix + _, wantErr = strconv.Unquote(got) // original input had trailing junk, reparse with only valid prefix want = got } if got != want || gotErr != wantErr { @@ -372,12 +373,12 @@ func testUnquote(t *testing.T, in, want string, wantErr error) { func BenchmarkUnquoteEasy(b *testing.B) { for i := 0; i < b.N; i++ { - Unquote(`"Give me a rock, paper and scissors and I will move the world."`) + strconv.Unquote(`"Give me a rock, paper and scissors and I will move the world."`) } } func BenchmarkUnquoteHard(b *testing.B) { for i := 0; i < b.N; i++ { - Unquote(`"\x47ive me a \x72ock, \x70aper and \x73cissors and \x49 will move the world."`) + strconv.Unquote(`"\x47ive me a \x72ock, \x70aper and \x73cissors and \x49 will move the world."`) } } diff --git a/gnovm/stdlibs/strconv/strconv_test.gno b/gnovm/stdlibs/strconv/strconv_test.gno index 5421ae84a93..e80eca207f7 100644 --- a/gnovm/stdlibs/strconv/strconv_test.gno +++ b/gnovm/stdlibs/strconv/strconv_test.gno @@ -2,9 +2,10 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package strconv +package strconv_test import ( + "strconv" "strings" "testing" ) @@ -21,24 +22,24 @@ var ( }{ {0, `AppendInt(localBuf[:0], 123, 10)`, func() { var localBuf [64]byte - AppendInt(localBuf[:0], 123, 10) + strconv.AppendInt(localBuf[:0], 123, 10) }}, - {0, `AppendInt(globalBuf[:0], 123, 10)`, func() { AppendInt(globalBuf[:0], 123, 10) }}, + {0, `AppendInt(globalBuf[:0], 123, 10)`, func() { strconv.AppendInt(globalBuf[:0], 123, 10) }}, {0, `AppendFloat(localBuf[:0], 1.23, 'g', 5, 64)`, func() { var localBuf [64]byte - AppendFloat(localBuf[:0], 1.23, 'g', 5, 64) + strconv.AppendFloat(localBuf[:0], 1.23, 'g', 5, 64) }}, - {0, `AppendFloat(globalBuf[:0], 1.23, 'g', 5, 64)`, func() { AppendFloat(globalBuf[:0], 1.23, 'g', 5, 64) }}, + {0, `AppendFloat(globalBuf[:0], 1.23, 'g', 5, 64)`, func() { strconv.AppendFloat(globalBuf[:0], 1.23, 'g', 5, 64) }}, // In practice we see 7 for the next one, but allow some slop. // Before pre-allocation in appendQuotedWith, we saw 39. - {10, `AppendQuoteToASCII(nil, oneMB)`, func() { AppendQuoteToASCII(nil, string(oneMB)) }}, - {0, `ParseFloat("123.45", 64)`, func() { ParseFloat("123.45", 64) }}, - {0, `ParseFloat("123.456789123456789", 64)`, func() { ParseFloat("123.456789123456789", 64) }}, + {10, `AppendQuoteToASCII(nil, oneMB)`, func() { strconv.AppendQuoteToASCII(nil, string(oneMB)) }}, + {0, `ParseFloat("123.45", 64)`, func() { strconv.ParseFloat("123.45", 64) }}, + {0, `ParseFloat("123.456789123456789", 64)`, func() { strconv.ParseFloat("123.456789123456789", 64) }}, {0, `ParseFloat("1.000000000000000111022302462515654042363166809082031251", 64)`, func() { - ParseFloat("1.000000000000000111022302462515654042363166809082031251", 64) + strconv.ParseFloat("1.000000000000000111022302462515654042363166809082031251", 64) }}, {0, `ParseFloat("1.0000000000000001110223024625156540423631668090820312500...001", 64)`, func() { - ParseFloat(nextToOne, 64) + strconv.ParseFloat(nextToOne, 64) }}, } ) @@ -125,11 +126,11 @@ func TestAllocationsFromBytes(t *testing.T) { */ func TestErrorPrefixes(t *testing.T) { - _, errInt := Atoi("INVALID") - _, errBool := ParseBool("INVALID") - _, errFloat := ParseFloat("INVALID", 64) - _, errInt64 := ParseInt("INVALID", 10, 64) - _, errUint64 := ParseUint("INVALID", 10, 64) + _, errInt := strconv.Atoi("INVALID") + _, errBool := strconv.ParseBool("INVALID") + _, errFloat := strconv.ParseFloat("INVALID", 64) + _, errInt64 := strconv.ParseInt("INVALID", 10, 64) + _, errUint64 := strconv.ParseUint("INVALID", 10, 64) vectors := []struct { err error // Input error @@ -143,7 +144,7 @@ func TestErrorPrefixes(t *testing.T) { } for _, v := range vectors { - nerr, ok := v.err.(*NumError) + nerr, ok := v.err.(*strconv.NumError) if !ok { t.Errorf("test %s, error was not a *NumError", v.want) continue diff --git a/gnovm/stdlibs/strings/printtrie.gno b/gnovm/stdlibs/strings/printtrie.gno new file mode 100644 index 00000000000..2a16c012eeb --- /dev/null +++ b/gnovm/stdlibs/strings/printtrie.gno @@ -0,0 +1,29 @@ +package strings + +func (r *Replacer) PrintTrie() string { + r.buildOnce() + gen := r.r.(*genericReplacer) + return gen.printNode(&gen.root, 0) +} + +func (r *genericReplacer) printNode(t *trieNode, depth int) (s string) { + if t.priority > 0 { + s += "+" + } else { + s += "-" + } + s += "\n" + + if t.prefix != "" { + s += Repeat(".", depth) + t.prefix + s += r.printNode(t.next, depth+len(t.prefix)) + } else if t.table != nil { + for b, m := range r.mapping { + if int(m) != r.tableSize && t.table[m] != nil { + s += Repeat(".", depth) + string([]byte{byte(b)}) + s += r.printNode(t.table[m], depth+1) + } + } + } + return +} diff --git a/gnovm/stdlibs/strings/printtrie_test.gno b/gnovm/stdlibs/strings/printtrie_test.gno index b5b387b9bca..a51208f4756 100644 --- a/gnovm/stdlibs/strings/printtrie_test.gno +++ b/gnovm/stdlibs/strings/printtrie_test.gno @@ -1,37 +1,10 @@ -package strings +package strings_test import ( + "strings" "testing" ) -func (r *Replacer) PrintTrie() string { - r.buildOnce() - gen := r.r.(*genericReplacer) - return gen.printNode(&gen.root, 0) -} - -func (r *genericReplacer) printNode(t *trieNode, depth int) (s string) { - if t.priority > 0 { - s += "+" - } else { - s += "-" - } - s += "\n" - - if t.prefix != "" { - s += Repeat(".", depth) + t.prefix - s += r.printNode(t.next, depth+len(t.prefix)) - } else if t.table != nil { - for b, m := range r.mapping { - if int(m) != r.tableSize && t.table[m] != nil { - s += Repeat(".", depth) + string([]byte{byte(b)}) - s += r.printNode(t.table[m], depth+1) - } - } - } - return -} - func TestGenericTrieBuilding(t *testing.T) { testCases := []struct{ in, out string }{ {"abc;abdef;abdefgh;xx;xy;z", `- @@ -79,13 +52,13 @@ func TestGenericTrieBuilding(t *testing.T) { } for _, tc := range testCases { - keys := Split(tc.in, ";") + keys := strings.Split(tc.in, ";") args := make([]string, len(keys)*2) for i, key := range keys { args[i*2] = key } - got := NewReplacer(args...).PrintTrie() + got := strings.NewReplacer(args...).PrintTrie() // Remove tabs from tc.out wantbuf := make([]byte, 0, len(tc.out)) for i := 0; i < len(tc.out); i++ { diff --git a/gnovm/stdlibs/testing/match.gno b/gnovm/stdlibs/testing/match.gno index 8b099f37624..fae9391dc79 100644 --- a/gnovm/stdlibs/testing/match.gno +++ b/gnovm/stdlibs/testing/match.gno @@ -8,7 +8,6 @@ package testing import ( "fmt" - "regexp" "strings" "unicode" ) @@ -35,7 +34,7 @@ func (m simpleMatch) matches(name []string) (ok, partial bool) { if i >= len(m) { break } - if ok, _ := regexp.MatchString(m[i], s); !ok { + if ok, _ := matchString(m[i], s); !ok { return false, false } } @@ -48,7 +47,7 @@ func (m simpleMatch) verify(name string) error { } // Verify filters before doing any processing. for i, s := range m { - if _, err := regexp.MatchString(s, "non-empty"); err != nil { + if _, err := matchString(s, "non-empty"); err != nil { return fmt.Errorf("element %d of %s (%q): %s", i, name, s, err) } } diff --git a/gnovm/stdlibs/testing/testing.gno b/gnovm/stdlibs/testing/testing.gno index fdafd9652ba..08082894178 100644 --- a/gnovm/stdlibs/testing/testing.gno +++ b/gnovm/stdlibs/testing/testing.gno @@ -322,6 +322,9 @@ func formatDur(dur int64) string { // used to calculate execution times; only present in testing stdlibs func unixNano() int64 +// used to filter tests, we can't directly use regexp here due to a cyclic import; only present in testing stdlibs +func matchString(pat, str string) (result bool, err error) + func tRunner(t *T, fn testingFunc, verbose bool) { if !t.shouldRun(t.name) { return diff --git a/gnovm/stdlibs/testing/testing.go b/gnovm/stdlibs/testing/testing.go index 2c2e1d69904..b7408b7cceb 100644 --- a/gnovm/stdlibs/testing/testing.go +++ b/gnovm/stdlibs/testing/testing.go @@ -1,6 +1,14 @@ package testing +import ( + "errors" +) + func X_unixNano() int64 { // only implemented in testing stdlibs return 0 } + +func X_matchString(pat, str string) (result bool, err error) { + return false, errors.New("only implemented in testing stdlibs") +} diff --git a/gnovm/tests/integ/invalid_gno_file/gno.mod b/gnovm/tests/integ/invalid_gno_file/gno.mod index 060e28b9dc4..f68f19aaf66 100644 --- a/gnovm/tests/integ/invalid_gno_file/gno.mod +++ b/gnovm/tests/integ/invalid_gno_file/gno.mod @@ -1 +1 @@ -module test +module invalid_gno_file diff --git a/gnovm/tests/integ/invalid_module_name/main.gno b/gnovm/tests/integ/invalid_module_name/main.gno new file mode 100644 index 00000000000..06ab7d0f9a3 --- /dev/null +++ b/gnovm/tests/integ/invalid_module_name/main.gno @@ -0,0 +1 @@ +package main diff --git a/gnovm/tests/stdlibs/README.md b/gnovm/tests/stdlibs/README.md index 16d5d171342..8742447e59a 100644 --- a/gnovm/tests/stdlibs/README.md +++ b/gnovm/tests/stdlibs/README.md @@ -4,3 +4,5 @@ This directory contains test-specific standard libraries. These are only available when testing gno code in `_test.gno` and `_filetest.gno` files. Re-declarations of functions already existing override the definitions of the normal stdlibs directory. + +Adding imports that don't exist in the corresponding normal stdlib is undefined behavior \ No newline at end of file diff --git a/gnovm/tests/stdlibs/generated.go b/gnovm/tests/stdlibs/generated.go index db5ecdec05d..4445d2467e8 100644 --- a/gnovm/tests/stdlibs/generated.go +++ b/gnovm/tests/stdlibs/generated.go @@ -333,6 +333,44 @@ var nativeFuncs = [...]NativeFunc{ )) }, }, + { + "testing", + "matchString", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("string")}, + {Name: gno.N("p1"), Type: gno.X("string")}, + }, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("bool")}, + {Name: gno.N("r1"), Type: gno.X("error")}, + }, + false, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 string + rp0 = reflect.ValueOf(&p0).Elem() + p1 string + rp1 = reflect.ValueOf(&p1).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 1, "")).TV, rp1) + + r0, r1 := testlibs_testing.X_matchString(p0, p1) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r1).Elem(), + )) + }, + }, { "unicode", "IsPrint", diff --git a/gnovm/tests/stdlibs/testing/native_testing.gno b/gnovm/tests/stdlibs/testing/native_testing.gno index 3076a679e77..64ed5edd61d 100644 --- a/gnovm/tests/stdlibs/testing/native_testing.gno +++ b/gnovm/tests/stdlibs/testing/native_testing.gno @@ -1,3 +1,5 @@ package testing func unixNano() int64 + +func matchString(pat, str string) (result bool, err error) diff --git a/gnovm/tests/stdlibs/testing/native_testing.go b/gnovm/tests/stdlibs/testing/native_testing.go index db681ad8a62..48c931c42ec 100644 --- a/gnovm/tests/stdlibs/testing/native_testing.go +++ b/gnovm/tests/stdlibs/testing/native_testing.go @@ -1,7 +1,26 @@ package testing -import "time" +import ( + "regexp" + "time" +) func X_unixNano() int64 { return time.Now().UnixNano() } + +func X_matchString(pat, str string) (result bool, err error) { + if matchRe == nil || matchPat != pat { + matchPat = pat + matchRe, err = regexp.Compile(matchPat) + if err != nil { + return + } + } + return matchRe.MatchString(str), nil +} + +var ( + matchPat string + matchRe *regexp.Regexp +) diff --git a/misc/genstd/mapping.go b/misc/genstd/mapping.go index 8b70e2f512d..f6130c17889 100644 --- a/misc/genstd/mapping.go +++ b/misc/genstd/mapping.go @@ -251,7 +251,7 @@ func resolveImport(imports []*ast.ImportSpec, ident string) string { // TODO: for simplicity, if i.Name is nil we assume the name to be == // to the last part of the import path. - // ideally, use importer to resolve package directory on user's FS and + // ideally, use packages to resolve package directory on user's FS and // resolve by parsing and reading package clause var name string if i.Name != nil {