Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: add txtar driver for gnoland integration test #1117

Merged
merged 49 commits into from
Oct 5, 2023
Merged
Show file tree
Hide file tree
Changes from 46 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
ad175a8
feat: add txtar driver to gnoland
gfanton Sep 12, 2023
9f4be75
chore: reload coverage
gfanton Sep 20, 2023
686ae5f
fix: increase sleep before gnoland is ready
gfanton Sep 26, 2023
76074e8
Merge branch 'master' into feat/gnoland-txtar-driver
gfanton Sep 28, 2023
89420bc
feat: export gnoland integration function
gfanton Sep 25, 2023
f887613
feat: txtar log improvements
gfanton Sep 28, 2023
30ba1de
fix: fixup render
gfanton Sep 28, 2023
cc1de0b
feat: add logger
gfanton Sep 29, 2023
b9d9156
chore: gno directory name definition
gfanton Sep 29, 2023
fc811ff
feat: upload logs as artifact
gfanton Sep 29, 2023
c0ed4b1
chore: lint
gfanton Oct 2, 2023
ed27557
Merge remote-tracking branch 'origin/master' into feat/gnoland-txtar-…
gfanton Oct 2, 2023
e808b91
fix: add sleep after addpkg command
gfanton Oct 3, 2023
5a7f784
chore: add comment on app gnorootdir
gfanton Oct 3, 2023
1b7d71c
chore: fix and update comments
gfanton Oct 3, 2023
a9687ca
Merge remote-tracking branch 'origin/master' into feat/gnoland-txtar-…
gfanton Oct 3, 2023
8e2516a
Update gno.land/cmd/gnoland/integration_test.go
gfanton Oct 3, 2023
c776213
chore: remove useless (not used) config argument for gnoland integration
gfanton Oct 3, 2023
9f82bcd
Merge branch 'master' into feat/gnoland-txtar-driver
gfanton Oct 3, 2023
4b57cd5
chore: comments, capitalize, punctuation
gfanton Oct 3, 2023
911320b
chore: correctly move sleep at the right place
gfanton Oct 4, 2023
bc08176
fix(docker): set GNOROOT in dockerfile instead of the docker integration
gfanton Oct 4, 2023
18046ea
fix(test): use mutex in defer
gfanton Oct 4, 2023
0cb9586
chore(log): set back %s instead of %+v
gfanton Oct 4, 2023
8d0f2d9
fix(tempdir): use only one tempdir call
gfanton Oct 4, 2023
221e216
feat: add `update_scripts` environment variable
gfanton Oct 4, 2023
3544390
feat: add partial `doc.go`
gfanton Oct 4, 2023
5f6f041
fix: remove sleep command inside testscripts
gfanton Oct 4, 2023
2f17114
Update gno.land/pkg/integration/testing_integration.go
gfanton Oct 4, 2023
0c589e2
fix: query reponse log
gfanton Oct 4, 2023
a265376
chore: centralize GuessGnoRootDir in gnoland
gfanton Oct 4, 2023
97c8048
fix: correctly handle unknown gnoland subcommand
gfanton Oct 4, 2023
348a410
fix: unused imports
gfanton Oct 4, 2023
9ea0422
fix: node stop not correctly handled
gfanton Oct 4, 2023
ebb9fc2
feat: add UPDATE_SCRIPTS to test and sync gnoland integration
gfanton Oct 4, 2023
358fbe2
chore: rename `CustomAppConfig` to `AppOptions` and `NewCustomApp` to…
gfanton Oct 4, 2023
aa253b3
chore: improve package doc.go
gfanton Oct 4, 2023
fbdf888
fix: check for first block, instead of sleepin
gfanton Oct 4, 2023
9f3f52f
REMOVE_ME: ci test with count 50
gfanton Oct 4, 2023
d3624dd
fix: use event system for beter node start handling
gfanton Oct 4, 2023
2436912
Revert "REMOVE_ME: ci test with count 50"
gfanton Oct 4, 2023
d66a4f6
chore: lint doc
gfanton Oct 4, 2023
c3ddec5
chore: add comment on `why a mutex will you say?`
gfanton Oct 4, 2023
f077906
chore: update doc.go
gfanton Oct 4, 2023
b8bd6b5
chore: add testing guide
gfanton Oct 4, 2023
4b80e80
chore: guide fix
gfanton Oct 4, 2023
977fe83
chore: minor fixes
gfanton Oct 4, 2023
c87a7a6
chore: use NewOptions instead of AppyDefault
gfanton Oct 4, 2023
57497cf
chore: sync golden files
gfanton Oct 4, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/workflows/gnoland.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,14 @@ jobs:
run: |
export GOPATH=$HOME/go
export GOTEST_FLAGS="-v -p 1 -timeout=30m -coverprofile=coverage.out -covermode=atomic"
export LOG_DIR="${{ runner.temp }}/logs/test-${{ matrix.goversion }}-gnoland"
make ${{ matrix.args }}
- name: Upload Test Log
if: always()
uses: actions/upload-artifact@v3
with:
name: logs-test-gnoland-go${{ matrix.goversion }}
path: ${{ runner.temp }}/logs/**/*.log
- uses: actions/upload-artifact@v3
if: ${{ runner.os == 'Linux' && matrix.goversion == '1.21.x' }}
with:
Expand Down
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ RUN rm -rf /opt/gno/src/.git

# runtime-base + runtime-tls
FROM debian:stable-slim AS runtime-base
ENV PATH="${PATH}:/opt/gno/bin"
ENV PATH="${PATH}:/opt/gno/bin" \
GNOROOT="/opt/gno/src"
WORKDIR /opt/gno/src
FROM runtime-base AS runtime-tls
RUN apt-get update && apt-get install -y expect ca-certificates && update-ca-certificates
Expand Down
86 changes: 86 additions & 0 deletions docs/testing_guide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Gnoland Testing Guide

This guide provides an overview of our testing practices and conventions. While most of our testing aligns with typical Go practices, there are exceptions and specifics you should be aware of.

## Standard Package Testing

For most packages, tests are written and executed in the standard Go manner:

- Tests are located alongside the code they test.
- The `go test` command can be used to execute tests.

However, as mentioned earlier, there are some exceptions. In the following sections, we will explore our specialized tests and how to work with them.

## Gno Filetests

**Location:** `gnovm/test/files`

These are our custom file-based tests tailored specifically for this project.

**Execution:**

From the gnovm directory, There are two main commands to run Gno filetests:

1. To test native files, use:
```
make _test.gnolang.native
```

2. To test standard libraries, use:
```
make _test.gnolang.stdlibs
```

**Golden Files Update:**

Golden files are references for expected outputs. Sometimes, after certain updates, these need to be synchronized. To do so:

1. For native tests:
```
make _test.gnolang.native.sync
```

2. For standard library tests:
```
make _test.gnolang.stdlibs.sync
```

## Integration Tests

**Location:** `gno.land/**/testdata`

From the gno.land directory, Integration tests are designed to ensure different parts of the project work cohesively. Specifically:

1. **InMemory Node Integration Testing:**
Found in `gno.land/cmd/gnoland/testdata`, these are dedicated to running integration tests against a genuine `gnoland` node.

2. **Integration Features Testing:**
Located in `gno.land/pkg/integration/testdata`, these tests target integrations specific commands.

These integration tests utilize the `testscript` package and follow the `txtar` file specifications.

**Documentation:**

- For general `testscript` package documentation, refer to: [testscript documentation](https://github.com/rogpeppe/go-internal/blob/v1.11.0/testscript/doc.go)

- For more specific details about our integration tests, consult our extended documentation: [gnoland integration documentation](https://github.com/gnolang/gno/blob/master/gno.land/pkg/integration/doc.go)

**Execution:**

To run the integration tests (alongside other packages):

```
make _test.pkgs
```

**Golden Files Update within txtar:**

For tests utilizing the `cmp` command inside `txtar` files, golden files can be synchronized using:

```
make _test.pkgs.sync
```

---

As the project evolves, this guide might be updated.
11 changes: 7 additions & 4 deletions gno.land/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,10 @@ test: _test.gnoland _test.gnoweb _test.gnokey _test.pkgs

GOTEST_FLAGS ?= -v -p 1 -timeout=30m

_test.gnoland:; go test $(GOTEST_FLAGS) ./cmd/gnoland
_test.gnoweb:; go test $(GOTEST_FLAGS) ./cmd/gnoweb
_test.gnokey:; go test $(GOTEST_FLAGS) ./cmd/gnokey
_test.pkgs:; go test $(GOTEST_FLAGS) ./pkg/...
_test.gnoland:; go test $(GOTEST_FLAGS) ./cmd/gnoland
_test.gnoweb:; go test $(GOTEST_FLAGS) ./cmd/gnoweb
_test.gnokey:; go test $(GOTEST_FLAGS) ./cmd/gnokey
_test.pkgs:; go test $(GOTEST_FLAGS) ./pkg/...
_test.pkgs.sync:; UPDATE_SCRIPTS=true go test $(GOTEST_FLAGS) ./pkg/...


3 changes: 2 additions & 1 deletion gno.land/cmd/gnokey/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
"fmt"
"os"

"github.com/gnolang/gno/tm2/pkg/commands"
"github.com/gnolang/gno/tm2/pkg/crypto/keys/client"
)

func main() {
cmd := client.NewRootCmd()
cmd := client.NewRootCmd(commands.NewDefaultIO())

Check warning on line 13 in gno.land/cmd/gnokey/main.go

View check run for this annotation

Codecov / codecov/patch

gno.land/cmd/gnokey/main.go#L13

Added line #L13 was not covered by tests

if err := cmd.ParseAndRun(context.Background(), os.Args[1:]); err != nil {
_, _ = fmt.Fprintf(os.Stderr, "%+v\n", err)
Expand Down
12 changes: 12 additions & 0 deletions gno.land/cmd/gnoland/integration_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package main

import (
"testing"

"github.com/gnolang/gno/gno.land/pkg/integration"
"github.com/rogpeppe/go-internal/testscript"
)

func TestTestdata(t *testing.T) {
testscript.Run(t, integration.SetupGnolandTestScript(t, "testdata"))
}
26 changes: 26 additions & 0 deletions gno.land/cmd/gnoland/testdata/addpkg.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# test for add package

## start a new node
gnoland start

## add bar.gno package located in $WORK directory as gno.land/r/foobar/bar
gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/foobar/bar -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1

## execute Render
gnokey maketx call -pkgpath gno.land/r/foobar/bar -func Render -gas-fee 1000000ugnot -gas-wanted 2000000 -args '' -broadcast -chainid=tendermint_test test1
Copy link
Member

@moul moul Sep 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally, we should make all parameters optional with defaults:

  • -chainid should always be set to "tendermint_test" since we only test this value in unit tests, not integration tests. I suggest setting the value by default and logging a warning if it differs.
  • -broadcast must be mandatory because we cannot combine the airgap-method with txtar.
  • -gas-wanted and -gas-fee should remain unchanged, in my opinion, without default values.

By implementing these changes, users can easily copy and paste commands from the terminal.

Copy link
Member Author

@gfanton gfanton Sep 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the precision !
I totally agree on doing that, but can I do this in a next iteration? for now I only add default arguments on the root level of gnokey (common arguments), which contain only:

  -config ...                                           
  -home  /Users/XXX/Library/Application Support/gno  
  -insecure-password-stdin=false                        
  -quiet=false                                          
  -remote 127.0.0.1:26657                              

args like chain-id are ont a second level, which require checking the kind of subcommand, ignore potential user args and finally properly inject it at the right place (not that difficult to do, but a bit fastidious).
If it's really mandatory for this PR to be merge I can take some time to add this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, let's keep for a next PR 👍

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think it's better if we don't have special defaults when running tests with txtar; this way we can ensure that any command from the txtar tests can just be copy pasted into a terminal.

alternative idea: have a GNOKEY_APPEND_FLAGS env variable which adds its value as arguments, before passing onto pkg/commands / ffcli?

(I agree on doing this in a next PR, just thought I'd point this out here as the discussion was already started)


## compare render
cmp stdout stdout.golden

-- bar.gno --
package bar

func Render(path string) string {
return "hello from foo"
}

-- stdout.golden --
("hello from foo" string)
OK!
GAS WANTED: 2000000
GAS USED: 69163
105 changes: 91 additions & 14 deletions gno.land/pkg/gnoland/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package gnoland

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

Expand All @@ -20,34 +22,68 @@ import (
"github.com/gnolang/gno/tm2/pkg/store/iavl"
)

type AppOptions struct {
DB dbm.DB
// `gnoRootDir` should point to the local location of the gno repository.
// It serves as the gno equivalent of GOROOT.
GnoRootDir string
gfanton marked this conversation as resolved.
Show resolved Hide resolved
SkipFailingGenesisTxs bool
Logger log.Logger
MaxCycles int64
}

func (c *AppOptions) ApplyDefault() {
gfanton marked this conversation as resolved.
Show resolved Hide resolved
if c.Logger == nil {
c.Logger = log.NewNopLogger()
}

if c.DB == nil {
c.DB = dbm.NewMemDB()
}

if c.GnoRootDir == "" {
c.GnoRootDir = GuessGnoRootDir()
}
}

func (c *AppOptions) validate() error {
if c.Logger == nil {
return fmt.Errorf("no logger provided")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: this can be a regular errors.New, since nothing is being formatted

Same nit for the other check in validate

}

if c.DB == nil {
return fmt.Errorf("no db provided")
}

return nil
}

// NewApp creates the GnoLand application.
func NewApp(rootDir string, skipFailingGenesisTxs bool, logger log.Logger, maxCycles int64) (abci.Application, error) {
// Get main DB.
db, err := dbm.NewDB("gnolang", dbm.GoLevelDBBackend, filepath.Join(rootDir, "data"))
if err != nil {
return nil, fmt.Errorf("error initializing database %q using path %q: %w", dbm.GoLevelDBBackend, rootDir, err)
func NewAppWithOptions(cfg AppOptions) (abci.Application, error) {
if err := cfg.validate(); err != nil {
return nil, err
}

// Capabilities keys.
mainKey := store.NewStoreKey("main")
baseKey := store.NewStoreKey("base")

// Create BaseApp.
baseApp := sdk.NewBaseApp("gnoland", logger, db, baseKey, mainKey)
baseApp := sdk.NewBaseApp("gnoland", cfg.Logger, cfg.DB, baseKey, mainKey)
baseApp.SetAppVersion("dev")

// Set mounts for BaseApp's MultiStore.
baseApp.MountStoreWithDB(mainKey, iavl.StoreConstructor, db)
baseApp.MountStoreWithDB(baseKey, dbadapter.StoreConstructor, db)
baseApp.MountStoreWithDB(mainKey, iavl.StoreConstructor, cfg.DB)
baseApp.MountStoreWithDB(baseKey, dbadapter.StoreConstructor, cfg.DB)

// Construct keepers.
acctKpr := auth.NewAccountKeeper(mainKey, ProtoGnoAccount)
bankKpr := bank.NewBankKeeper(acctKpr)
stdlibsDir := filepath.Join("..", "gnovm", "stdlibs")
vmKpr := vm.NewVMKeeper(baseKey, mainKey, acctKpr, bankKpr, stdlibsDir, maxCycles)
stdlibsDir := filepath.Join(cfg.GnoRootDir, "gnovm", "stdlibs")
gfanton marked this conversation as resolved.
Show resolved Hide resolved
vmKpr := vm.NewVMKeeper(baseKey, mainKey, acctKpr, bankKpr, stdlibsDir, cfg.MaxCycles)

// Set InitChainer
baseApp.SetInitChainer(InitChainer(baseApp, acctKpr, bankKpr, skipFailingGenesisTxs))
baseApp.SetInitChainer(InitChainer(baseApp, acctKpr, bankKpr, cfg.SkipFailingGenesisTxs))

// Set AnteHandler
authOptions := auth.AnteOptions{
Expand Down Expand Up @@ -88,6 +124,24 @@ func NewApp(rootDir string, skipFailingGenesisTxs bool, logger log.Logger, maxCy
return baseApp, nil
}

// NewApp creates the GnoLand application.
func NewApp(dataRootDir string, skipFailingGenesisTxs bool, logger log.Logger, maxCycles int64) (abci.Application, error) {
var err error

var cfg AppOptions
cfg.ApplyDefault()

// Get main DB.
cfg.DB, err = dbm.NewDB("gnolang", dbm.GoLevelDBBackend, filepath.Join(dataRootDir, "data"))
if err != nil {
return nil, fmt.Errorf("error initializing database %q using path %q: %w", dbm.GoLevelDBBackend, dataRootDir, err)
}

cfg.Logger = logger

return NewAppWithOptions(cfg)
}

// InitChainer returns a function that can initialize the chain with genesis.
func InitChainer(baseApp *sdk.BaseApp, acctKpr auth.AccountKeeperI, bankKpr bank.BankKeeperI, skipFailingGenesisTxs bool) func(sdk.Context, abci.RequestInitChain) abci.ResponseInitChain {
return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
Expand All @@ -107,14 +161,15 @@ func InitChainer(baseApp *sdk.BaseApp, acctKpr auth.AccountKeeperI, bankKpr bank
for i, tx := range genState.Txs {
res := baseApp.Deliver(tx)
if res.IsErr() {
fmt.Println("ERROR LOG:", res.Log)
fmt.Println("#", i, string(amino.MustMarshalJSON(tx)))
ctx.Logger().Error("LOG", res.Log)
ctx.Logger().Error("#", i, string(amino.MustMarshalJSON(tx)))

// NOTE: comment out to ignore.
if !skipFailingGenesisTxs {
panic(res.Error)
}
} else {
fmt.Println("SUCCESS:", string(amino.MustMarshalJSON(tx)))
ctx.Logger().Info("SUCCESS:", string(amino.MustMarshalJSON(tx)))
}
}
// Done!
Expand Down Expand Up @@ -146,3 +201,25 @@ func EndBlocker(vmk vm.VMKeeperI) func(ctx sdk.Context, req abci.RequestEndBlock
return abci.ResponseEndBlock{}
}
}

func GuessGnoRootDir() string {
var rootdir string

// First try to get the root directory from the GNOROOT environment variable.
if rootdir = os.Getenv("GNOROOT"); rootdir != "" {
return filepath.Clean(rootdir)
}

if gobin, err := exec.LookPath("go"); err == nil {
// If GNOROOT is not set, try to guess the root directory using the `go list` command.
cmd := exec.Command(gobin, "list", "-m", "-mod=mod", "-f", "{{.Dir}}", "github.com/gnolang/gno")
out, err := cmd.CombinedOutput()
if err != nil {
panic(fmt.Errorf("invalid gno directory %q: %w", rootdir, err))
}

return strings.TrimSpace(string(out))
}

panic("no go binary available, unable to determine gno root-dir path")
}
Loading
Loading