diff --git a/docs/assets/how-to-guides/porting-solidity-to-gno/porting-6.gno b/docs/assets/how-to-guides/porting-solidity-to-gno/porting-6.gno index b544d0017c4..f95d7e8b50a 100644 --- a/docs/assets/how-to-guides/porting-solidity-to-gno/porting-6.gno +++ b/docs/assets/how-to-guides/porting-solidity-to-gno/porting-6.gno @@ -1,41 +1,42 @@ -// Bid Function Test - Send Coin -func TestBidCoins(t *testing.T) { - // Sending two types of coins - std.TestSetOrigCaller(bidder01) - std.TestSetOrigSend(std.Coins{{"ugnot", 0}, {"test", 1}}, nil) - shouldPanic(t, Bid) - - // Sending lower amount than the current highest bid - std.TestSetOrigCaller(bidder01) - std.TestSetOrigSend(std.Coins{{"ugnot", 0}}, nil) - shouldPanic(t, Bid) - - // Sending more amount than the current highest bid (exceeded) - std.TestSetOrigCaller(bidder01) - std.TestSetOrigSend(std.Coins{{"ugnot", 1}}, nil) - shouldNoPanic(t, Bid) -} - -// Bid Function Test - Bid by two or more people -func TestBidCoins(t *testing.T) { - // bidder01 bidding with 1 coin - std.TestSetOrigCaller(bidder01) - std.TestSetOrigSend(std.Coins{{"ugnot", 1}}, nil) - shouldNoPanic(t, Bid) - shouldEqual(t, highestBid, 1) - shouldEqual(t, highestBidder, bidder01) - shouldEqual(t, pendingReturns.Size(), 0) - - // bidder02 bidding with 1 coin - std.TestSetOrigCaller(bidder02) - std.TestSetOrigSend(std.Coins{{"ugnot", 1}}, nil) - shouldPanic(t, Bid) - - // bidder02 bidding with 2 coins - std.TestSetOrigCaller(bidder02) - std.TestSetOrigSend(std.Coins{{"ugnot", 2}}, nil) - shouldNoPanic(t, Bid) - shouldEqual(t, highestBid, 2) - shouldEqual(t, highestBidder, bidder02) - shouldEqual(t, pendingReturns.Size(), 1) -} +docs/assets/how-to-guides/porting-solidity-to-gno/porting-13.gno: std.TestSetOrigSend(std.Coins{{"ugnot", 0}, {"test", 1}}, nil) +docs/assets/how-to-guides/porting-solidity-to-gno/porting-13.gno: std.TestSetOrigSend(std.Coins{{"ugnot", 0}}, nil) +docs/assets/how-to-guides/porting-solidity-to-gno/porting-13.gno: std.TestSetOrigSend(std.Coins{{"ugnot", 1}}, nil) +docs/assets/how-to-guides/porting-solidity-to-gno/porting-13.gno: std.TestSetOrigSend(std.Coins{{"ugnot", 1}}, nil) +docs/assets/how-to-guides/porting-solidity-to-gno/porting-13.gno: std.TestSetOrigSend(std.Coins{{"ugnot", 2}}, nil) +docs/assets/how-to-guides/porting-solidity-to-gno/porting-6.gno: std.TestSetOrigSend(std.Coins{{"ugnot", 0}, {"test", 1}}, nil) +docs/assets/how-to-guides/porting-solidity-to-gno/porting-6.gno: std.TestSetOrigSend(std.Coins{{"ugnot", 0}}, nil) +docs/assets/how-to-guides/porting-solidity-to-gno/porting-6.gno: std.TestSetOrigSend(std.Coins{{"ugnot", 1}}, nil) +docs/assets/how-to-guides/porting-solidity-to-gno/porting-6.gno: std.TestSetOrigSend(std.Coins{{"ugnot", 1}}, nil) +docs/assets/how-to-guides/porting-solidity-to-gno/porting-6.gno: std.TestSetOrigSend(std.Coins{{"ugnot", 1}}, nil) +docs/assets/how-to-guides/porting-solidity-to-gno/porting-6.gno: std.TestSetOrigSend(std.Coins{{"ugnot", 2}}, nil) +docs/how-to-guides/porting-solidity-to-gno.md: std.TestSetOrigSend(std.Coins{{"ugnot", 0}, {"test", 1}}, nil) +docs/how-to-guides/porting-solidity-to-gno.md: std.TestSetOrigSend(std.Coins{{"ugnot", 0}}, nil) +docs/how-to-guides/porting-solidity-to-gno.md: std.TestSetOrigSend(std.Coins{{"ugnot", 1}}, nil) +docs/how-to-guides/porting-solidity-to-gno.md: std.TestSetOrigSend(std.Coins{{"ugnot", 1}}, nil) +docs/how-to-guides/porting-solidity-to-gno.md: std.TestSetOrigSend(std.Coins{{"ugnot", 1}}, nil) +docs/how-to-guides/porting-solidity-to-gno.md: std.TestSetOrigSend(std.Coins{{"ugnot", 2}}, nil) +docs/how-to-guides/porting-solidity-to-gno.md: std.TestSetOrigSend(std.Coins{{"ugnot", 0}, {"test", 1}}, nil) +docs/how-to-guides/porting-solidity-to-gno.md: std.TestSetOrigSend(std.Coins{{"ugnot", 0}}, nil) +docs/how-to-guides/porting-solidity-to-gno.md: std.TestSetOrigSend(std.Coins{{"ugnot", 1}}, nil) +docs/how-to-guides/porting-solidity-to-gno.md: std.TestSetOrigSend(std.Coins{{"ugnot", 1}}, nil) +docs/how-to-guides/porting-solidity-to-gno.md: std.TestSetOrigSend(std.Coins{{"ugnot", 2}}, nil) +docs/reference/stdlibs/std/testing.md:std.TestSetOrigSend(sent, spent Coins) +examples/gno.land/r/demo/banktest/z_0_filetest.gno: std.TestSetOrigSend(std.Coins{{"ugnot", 100_000_000}}, nil) +examples/gno.land/r/demo/banktest/z_1_filetest.gno: std.TestSetOrigSend(std.Coins{{"ugnot", 100000000}}, nil) +examples/gno.land/r/demo/banktest/z_2_filetest.gno: std.TestSetOrigSend(std.Coins{{"ugnot", 100000000}}, nil) +examples/gno.land/r/demo/boards/z_12_a_filetest.gno: std.TestSetOrigSend(std.Coins{{"ugnot", 9000000}}, nil) +examples/gno.land/r/demo/boards/z_5_b_filetest.gno: std.TestSetOrigSend(std.Coins{{"ugnot", 9000000}}, nil) +examples/gno.land/r/demo/boards/z_5_c_filetest.gno: std.TestSetOrigSend(std.Coins{{"ugnot", 101000000}}, nil) +examples/gno.land/r/demo/boards/z_5_d_filetest.gno: std.TestSetOrigSend(std.Coins{{"ugnot", 9000000}}, nil) +examples/gno.land/r/demo/groups/z_1_c_filetest.gno: std.TestSetOrigSend(std.Coins{{"ugnot", 9000000}}, nil) +examples/gno.land/r/demo/groups/z_2_d_filetest.gno: std.TestSetOrigSend(std.Coins{{"ugnot", 9000000}}, nil) +examples/gno.land/r/demo/groups/z_2_g_filetest.gno: std.TestSetOrigSend(std.Coins{{"ugnot", 9000000}}, nil) +examples/gno.land/r/demo/users/users_test.gno: std.TestSetOrigSend("199000000ugnot") +examples/gno.land/r/demo/users/z_0_filetest.gno: std.TestSetOrigSend(std.Coins{std.NewCoin("dontcare", 1)}, nil) +examples/gno.land/r/demo/users/z_3_filetest.gno: std.TestSetOrigSend(std.Coins{{"dontcare", 1}}, nil) +examples/gno.land/r/demo/users/z_4_filetest.gno: std.TestSetOrigSend(std.Coins{{"dontcare", 1}}, nil) +examples/gno.land/r/demo/users/z_5_filetest.gno: std.TestSetOrigSend(std.Coins{{"dontcare", 1}}, nil) +examples/gno.land/r/demo/users/z_7_filetest.gno: std.TestSetOrigSend(std.Coins{{"dontcare", 1}}, nil) +examples/gno.land/r/demo/users/z_7b_filetest.gno: std.TestSetOrigSend(std.Coins{{"dontcare", 1}}, nil) +examples/gno.land/r/demo/users/z_8_filetest.gno: std.TestSetOrigSend(std.Coins{{"dontcare", 1}}, nil) +examples/gno.land/r/demo/wugnot/z0_filetest.gno: std.TestSetOrigSend(std.Coins{{"ugnot", 123_400}}, nil) diff --git a/examples/gno.land/p/demo/entropy/entropy.gno b/examples/gno.land/p/demo/entropy/entropy.gno new file mode 100644 index 00000000000..1dd827940d1 --- /dev/null +++ b/examples/gno.land/p/demo/entropy/entropy.gno @@ -0,0 +1,85 @@ +// Entropy generates fully deterministic, cost-effective, and hard to guess +// numbers. +// +// It is designed both for for single-usage, like seeding math/rand or for being +// reused which increases the entropy and its cost effectiveness. +// +// Disclaimer: this package is unsafe and won't prevent others to guess values +// in advance. +// +// It uses the Bernstein's hash djb2 to be CPU-cycle efficient. +package entropy + +import ( + "std" + "time" +) + +type Instance struct { + value int64 +} + +func New() *Instance { + r := Instance{value: 5381} + r.addEntropy() + return &r +} + +func FromSeed(seed int64) *Instance { + r := Instance{value: seed} + r.addEntropy() + return &r +} + +func (i *Instance) Seed() int64 { + return i.value +} + +func (i *Instance) djb2String(input string) { + for _, c := range input { + i.djb2Int64(int64(c)) + } +} + +// super fast random algorithm. +// http://www.cse.yorku.ca/~oz/hash.html +func (i *Instance) djb2Int64(input int64) { + i.value = (i.value << 5) + i.value + input +} + +// AddEntropy uses various runtime variables to add entropy to the existing seed. +func (i *Instance) addEntropy() { + // FIXME: reapply the 5381 initial value? + + // inherit previous entropy + // nothing to do + + // handle callers + { + caller1 := std.GetCallerAt(1).String() + i.djb2String(caller1) + caller2 := std.GetCallerAt(2).String() + i.djb2String(caller2) + } + + // height + { + height := std.GetHeight() + i.djb2Int64(height) + } + + // time + { + secs := time.Now().Second() + i.djb2Int64(int64(secs)) + nsecs := time.Now().Nanosecond() + i.djb2Int64(int64(nsecs)) + } + + // FIXME: compute other hard-to-guess but deterministic variables, like real gas? +} + +func (i *Instance) Value() int64 { + i.addEntropy() + return i.value +} diff --git a/examples/gno.land/p/demo/entropy/entropy_test.gno b/examples/gno.land/p/demo/entropy/entropy_test.gno new file mode 100644 index 00000000000..013709bc4e9 --- /dev/null +++ b/examples/gno.land/p/demo/entropy/entropy_test.gno @@ -0,0 +1,48 @@ +package entropy + +import ( + "fmt" + "std" + "strconv" + "strings" + "testing" +) + +func TestInstance(t *testing.T) { + instance := New() + if instance == nil { + t.Errorf("instance should not be nil") + } +} + +func TestInstanceValue(t *testing.T) { + baseEntropy := New() + baseResult := computeValue(t, baseEntropy) + + sameHeightEntropy := New() + sameHeightResult := computeValue(t, sameHeightEntropy) + + if baseResult != sameHeightResult { + t.Errorf("should have the same result: new=%s, base=%s", sameHeightResult, baseResult) + } + + std.TestSkipHeights(1) + differentHeightEntropy := New() + differentHeightResult := computeValue(t, differentHeightEntropy) + + if baseResult == differentHeightResult { + t.Errorf("should have different result: new=%s, base=%s", differentHeightResult, baseResult) + } +} + +func computeValue(t *testing.T, r *Instance) string { + t.Helper() + + out := "" + for i := 0; i < 10; i++ { + val := int(r.Value()) + out += strconv.Itoa(val) + " " + } + + return out +} diff --git a/examples/gno.land/p/demo/entropy/gno.mod b/examples/gno.land/p/demo/entropy/gno.mod new file mode 100644 index 00000000000..9a6db8f5b61 --- /dev/null +++ b/examples/gno.land/p/demo/entropy/gno.mod @@ -0,0 +1 @@ +module gno.land/p/demo/entropy diff --git a/examples/gno.land/p/demo/entropy/z_filetest.gno b/examples/gno.land/p/demo/entropy/z_filetest.gno new file mode 100644 index 00000000000..b09f950b179 --- /dev/null +++ b/examples/gno.land/p/demo/entropy/z_filetest.gno @@ -0,0 +1,56 @@ +package main + +import ( + "std" + + "gno.land/p/demo/entropy" +) + +func main() { + // initial + println("---") + r := entropy.New() + println(r.Value()) + println(r.Value()) + println(r.Value()) + println(r.Value()) + println(r.Value()) + + // should be the same + println("---") + r = entropy.New() + println(r.Value()) + println(r.Value()) + println(r.Value()) + println(r.Value()) + println(r.Value()) + + std.TestSkipHeights(1) + println("---") + r = entropy.New() + println(r.Value()) + println(r.Value()) + println(r.Value()) + println(r.Value()) + println(r.Value()) +} + +// Output: +// --- +// 2206404796634629535 +// 6369783909128776508 +// -273967328977352263 +// 3685013872379606294 +// -321443877504368301 +// --- +// 2206404796634629535 +// 6369783909128776508 +// -273967328977352263 +// 3685013872379606294 +// -321443877504368301 +// --- +// 2389594832880495019 +// 2483785090669486254 +// 902493904282098385 +// 5349860799650907156 +// 3037689170578704503 diff --git a/examples/gno.land/p/demo/uassert/uassert.gno b/examples/gno.land/p/demo/uassert/uassert.gno index 61885574360..72fd1f276a9 100644 --- a/examples/gno.land/p/demo/uassert/uassert.gno +++ b/examples/gno.land/p/demo/uassert/uassert.gno @@ -126,7 +126,7 @@ func Equal(t TestingT, expected, actual interface{}, msgs ...string) bool { if av, ok := actual.(string); ok { equal = ev == av ok_ = true - es, as = ev, as + es, as = ev, av } case std.Address: if av, ok := actual.(std.Address); ok { diff --git a/examples/gno.land/p/demo/wordlist/adjective.gno b/examples/gno.land/p/demo/wordlist/adjective.gno new file mode 100644 index 00000000000..b034480fac7 --- /dev/null +++ b/examples/gno.land/p/demo/wordlist/adjective.gno @@ -0,0 +1,23 @@ +package wordlist + +import "math/rand" + +var ( + adjectives = []string{ + "angry", + "brave", + "cocky", + "goofy", + "happy", + "jolly", + "lucid", + "nifty", + "sharp", + "stoic", + } + adjectivesL = len(adjectives) +) + +func RandomAdjective(r *rand.Rand) string { + return adjectives[r.IntN(adjectivesL)] +} diff --git a/examples/gno.land/p/demo/wordlist/color.gno b/examples/gno.land/p/demo/wordlist/color.gno new file mode 100644 index 00000000000..754bc866277 --- /dev/null +++ b/examples/gno.land/p/demo/wordlist/color.gno @@ -0,0 +1,28 @@ +package wordlist + +import "math/rand" + +var ( + colors = []string{ + "aqua", + "black", + "blue", + "fuchsia", + "gray", + "green", + "lime", + "maroon", + "navy", + "olive", + "purple", + "silver", + "teal", + "white", + "yellow", + } + colorsL = len(colors) +) + +func RandomColor(r *rand.Rand) string { + return colors[r.IntN(colorsL)] +} diff --git a/examples/gno.land/p/demo/wordlist/gno.mod b/examples/gno.land/p/demo/wordlist/gno.mod new file mode 100644 index 00000000000..e093c7c6a69 --- /dev/null +++ b/examples/gno.land/p/demo/wordlist/gno.mod @@ -0,0 +1 @@ +module gno.land/p/demo/wordlist diff --git a/examples/gno.land/p/demo/wordlist/job.gno b/examples/gno.land/p/demo/wordlist/job.gno new file mode 100644 index 00000000000..673baf20d9d --- /dev/null +++ b/examples/gno.land/p/demo/wordlist/job.gno @@ -0,0 +1,24 @@ +package wordlist + +import "math/rand" + +var ( + jobs = []string{ + "architect", + "baker", + "cleaner", + "cooker", + "doctor", + "designer", + "developer", + "engineer", + "officer", + "priest", + "teacher", + } + jobsL = len(jobs) +) + +func RandomJob(r *rand.Rand) string { + return jobs[r.IntN(jobsL)] +} diff --git a/examples/gno.land/r/demo/art/gnoface/gno.mod b/examples/gno.land/r/demo/art/gnoface/gno.mod index f2d3ddebadc..d3c8feb3c84 100644 --- a/examples/gno.land/r/demo/art/gnoface/gno.mod +++ b/examples/gno.land/r/demo/art/gnoface/gno.mod @@ -1,3 +1,6 @@ module gno.land/r/demo/art/gnoface -require gno.land/p/demo/ufmt v0.0.0-latest +require ( + gno.land/p/demo/entropy v0.0.0-latest + gno.land/p/demo/ufmt v0.0.0-latest +) diff --git a/examples/gno.land/r/demo/art/gnoface/gnoface.gno b/examples/gno.land/r/demo/art/gnoface/gnoface.gno index 9e85c5c7387..e8239412c4f 100644 --- a/examples/gno.land/r/demo/art/gnoface/gnoface.gno +++ b/examples/gno.land/r/demo/art/gnoface/gnoface.gno @@ -6,11 +6,12 @@ import ( "strconv" "strings" + "gno.land/p/demo/entropy" "gno.land/p/demo/ufmt" ) func Render(path string) string { - seed := uint64(std.GetHeight()) + seed := uint64(entropy.New().Value()) path = strings.TrimSpace(path) if path != "" { diff --git a/examples/gno.land/r/demo/users/gno.mod b/examples/gno.land/r/demo/users/gno.mod index a2ee2ea86ba..a6c26aa8fc9 100644 --- a/examples/gno.land/r/demo/users/gno.mod +++ b/examples/gno.land/r/demo/users/gno.mod @@ -2,5 +2,9 @@ module gno.land/r/demo/users require ( gno.land/p/demo/avl v0.0.0-latest + gno.land/p/demo/entropy v0.0.0-latest + gno.land/p/demo/uassert v0.0.0-latest + gno.land/p/demo/ufmt v0.0.0-latest gno.land/p/demo/users v0.0.0-latest + gno.land/p/demo/wordlist v0.0.0-latest ) diff --git a/examples/gno.land/r/demo/users/users.gno b/examples/gno.land/r/demo/users/users.gno index 5b4b8ec2c14..188c825af14 100644 --- a/examples/gno.land/r/demo/users/users.gno +++ b/examples/gno.land/r/demo/users/users.gno @@ -1,39 +1,92 @@ package users import ( + "errors" + "math/rand" "regexp" "std" "strconv" "strings" "gno.land/p/demo/avl" + "gno.land/p/demo/entropy" + "gno.land/p/demo/ufmt" "gno.land/p/demo/users" + "gno.land/p/demo/wordlist" ) //---------------------------------------- // State var ( - admin std.Address = "g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj" - name2User avl.Tree // Name -> *users.User - addr2User avl.Tree // std.Address -> *users.User - invites avl.Tree // string(inviter+":"+invited) -> true - counter int // user id counter - minFee int64 = 200 * 1000000 // minimum gnot must be paid to register. - maxFeeMult int64 = 10 // maximum multiples of minFee accepted. + admin std.Address = "g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj" + name2User avl.Tree // Name -> *users.User + addr2User avl.Tree // std.Address -> *users.User + invites avl.Tree // string(inviter+":"+invited) -> true + counter int // user id counter + minFee int64 = 200 * 1000000 // minimum gnot must be paid to register. + maxFeeMult int64 = 10 // maximum multiples of minFee accepted. + ent *entropy.Instance = entropy.New() ) +var ErrAddressAlreadyRegistered = errors.New("address already registered") + //---------------------------------------- // Top-level functions -func Register(inviter std.Address, name string, profile string) { - // assert CallTx call. +// ClaimDefaultName registers a random name for free. +func ClaimDefaultName(profile string) string { std.AssertOriginCall() - // assert invited or paid. - caller := std.GetCallerAt(2) - if caller != std.GetOrigCaller() { - panic("should not happen") // because std.AssertOrigCall(). + caller := std.PrevRealm().Addr() + + _, ok := addr2User.Get(caller.String()) + if ok { + panic(ErrAddressAlreadyRegistered) + } + + seed := uint64(ent.Value()) + r := rand.New(rand.NewPCG(seed, 0xbadcafe)) + + var name string + for { // gas is the limit. + var ( + adjective = wordlist.RandomAdjective(r) + job = wordlist.RandomJob(r) + color = wordlist.RandomColor(r) + nb = r.IntN(100) + ) + name = ufmt.Sprintf("%s_%s_%s_%d%d", adjective, color, job, nb/10, nb%10) + _, ok := name2User.Get(name) + if !ok { + break + } + } + + // register. + counter++ + user := &users.User{ + Address: caller, + Name: name, + Profile: profile, + Number: counter, } + name2User.Set(name, user) + addr2User.Set(caller.String(), user) + + return name +} + +// Register registers a chosen name. +func Register(inviter std.Address, name string, profile string) { + std.AssertOriginCall() + register(inviter, name, profile) +} + +func register(inviter std.Address, name string, profile string) { + // XXX: Register could override an existing default name, but we need to implement symlnks. + + // assert CallTx call. + caller := std.PrevRealm().Addr() sentCoins := std.GetOrigSend() minCoin := std.NewCoin("ugnot", minFee) if inviter == "" { @@ -58,11 +111,11 @@ func Register(inviter std.Address, name string, profile string) { // assert not already registered. _, ok := name2User.Get(name) if ok { - panic("name already registered") + panic("name already exists") } _, ok = addr2User.Get(caller.String()) if ok { - panic("address already registered") + panic(ErrAddressAlreadyRegistered) } // assert name is valid. if !reName.MatchString(name) { @@ -78,6 +131,7 @@ func Register(inviter std.Address, name string, profile string) { } } } + // register. counter++ user := &users.User{ diff --git a/examples/gno.land/r/demo/users/users_test.gno b/examples/gno.land/r/demo/users/users_test.gno new file mode 100644 index 00000000000..e783bf0a67f --- /dev/null +++ b/examples/gno.land/r/demo/users/users_test.gno @@ -0,0 +1,49 @@ +package users + +import ( + "std" + "testing" + + "gno.land/p/demo/entropy" + "gno.land/p/demo/testutils" + "gno.land/p/demo/uassert" +) + +var ( + alice = testutils.TestAddress("alice") + bob = testutils.TestAddress("bob") +) + +/* + std.TestSetRealm(std.NewUserRealm(alice)) + std.TestSetOrigCaller(alice) + std.TestIssueCoins(alice, std.Coins{{"ugnot", 199000000}}) + std.TestSetOrigSend(std.Coins{{"ugnot", 199000000}}, nil) +*/ + +func TestRegister_successWithPayment(t *testing.T) { + std.TestSetOrigSend(std.Coins{std.NewCoin("ugnot", 200000000)}, nil) + Register("", "gnouser", "myprofile") +} + +func TestRegister_notEnoughPayment(t *testing.T) { + std.TestSetOrigSend(std.Coins{std.NewCoin("ugnot", 199000000)}, nil) + uassert.PanicsWithMessage(t, "payment must not be less than 200000000", func() { + register("", "gnouser", "myprofile") + }) +} + +func TestRegister_invalidDenom(t *testing.T) { + std.TestSetOrigSend(std.Coins{std.NewCoin("dontcare", 1)}, nil) + uassert.PanicsWithMessage(t, "incompatible coin denominations: dontcare, ugnot", func() { + register("", "gnouser", "myprofile") + }) +} + +func TestClaimDefaultName(t *testing.T) { + ent = entropy.FromSeed(42) // fixed entropy + profile := "hello world" + name := ClaimDefaultName(profile) + expected := "nifty_teal_engineer_51" // this can easily break if p/demo/wordlist is modified + uassert.Equal(t, expected, name) +} diff --git a/examples/gno.land/r/demo/users/z_0_b_filetest.gno b/examples/gno.land/r/demo/users/z_0_b_filetest.gno deleted file mode 100644 index 9095057076c..00000000000 --- a/examples/gno.land/r/demo/users/z_0_b_filetest.gno +++ /dev/null @@ -1,15 +0,0 @@ -package main - -// SEND: 199000000ugnot - -import ( - "gno.land/r/demo/users" -) - -func main() { - users.Register("", "gnouser", "my profile") - println("done") -} - -// Error: -// payment must not be less than 200000000 diff --git a/examples/gno.land/r/demo/users/z_0_filetest.gno b/examples/gno.land/r/demo/users/z_0_filetest.gno deleted file mode 100644 index cbb2e9209f4..00000000000 --- a/examples/gno.land/r/demo/users/z_0_filetest.gno +++ /dev/null @@ -1,16 +0,0 @@ -package main - -import ( - "std" - - "gno.land/r/demo/users" -) - -func main() { - std.TestSetOrigSend(std.Coins{std.NewCoin("dontcare", 1)}, nil) - users.Register("", "gnouser", "my profile") - println("done") -} - -// Error: -// incompatible coin denominations: dontcare, ugnot diff --git a/examples/gno.land/r/demo/users/z_1_filetest.gno b/examples/gno.land/r/demo/users/z_1_filetest.gno deleted file mode 100644 index a1c7e682022..00000000000 --- a/examples/gno.land/r/demo/users/z_1_filetest.gno +++ /dev/null @@ -1,15 +0,0 @@ -package main - -// SEND: 2000000000ugnot - -import ( - "gno.land/r/demo/users" -) - -func main() { - users.Register("", "gnouser", "my profile") - println("done") -} - -// Output: -// done diff --git a/gnovm/cmd/gno/test.go b/gnovm/cmd/gno/test.go index 5884463a552..e25ebf85aa9 100644 --- a/gnovm/cmd/gno/test.go +++ b/gnovm/cmd/gno/test.go @@ -277,25 +277,7 @@ func gnoTestPkg( // run test files in pkg if len(tfiles.Files) > 0 { - testStore := tests.TestStore( - rootDir, "", - stdin, stdout, stderr, - mode, - ) - if verbose { - testStore.SetLogStoreOps(true) - } - - m := tests.TestMachine(testStore, stdout, gnoPkgPath) - if printRuntimeMetrics { - // from tm2/pkg/sdk/vm/keeper.go - // XXX: make maxAllocTx configurable. - maxAllocTx := int64(500 * 1000 * 1000) - - m.Alloc = gno.NewAllocator(maxAllocTx) - } - m.RunMemPackage(memPkg, true) - err := runTestFiles(m, tfiles, memPkg.Name, verbose, printRuntimeMetrics, runFlag, io) + err := runTestFiles(cfg, memPkg, tfiles, verbose, printRuntimeMetrics, runFlag, io) if err != nil { errs = multierr.Append(errs, err) } @@ -312,8 +294,6 @@ func gnoTestPkg( testStore.SetLogStoreOps(true) } - m := tests.TestMachine(testStore, stdout, testPkgName) - memFiles := make([]*std.MemFile, 0, len(ifiles.FileNames())+1) for _, f := range memPkg.Files { for _, ifileName := range ifiles.FileNames() { @@ -323,13 +303,13 @@ func gnoTestPkg( } } } + memPkg := &std.MemPackage{ + Name: testPkgName, + Files: memFiles, + Path: pkgPath + "_test", + } - memPkg.Files = memFiles - memPkg.Name = testPkgName - memPkg.Path = memPkg.Path + "_test" - m.RunMemPackage(memPkg, true) - - err := runTestFiles(m, ifiles, testPkgName, verbose, printRuntimeMetrics, runFlag, io) + err := runTestFiles(cfg, memPkg, ifiles, verbose, printRuntimeMetrics, runFlag, io) if err != nil { errs = multierr.Append(errs, err) } @@ -414,19 +394,20 @@ func pkgPathFromRootDir(pkgPath, rootDir string) string { } func runTestFiles( - m *gno.Machine, + cfg *testCfg, + memPkg *std.MemPackage, files *gno.FileSet, - pkgName string, verbose bool, printRuntimeMetrics bool, runFlag string, io commands.IO, ) (errs error) { - defer func() { - if r := recover(); r != nil { - errs = multierr.Append(fmt.Errorf("panic: %v\nstack:\n%v\ngno machine: %v", r, string(debug.Stack()), m.String()), errs) - } - }() + pkgName := memPkg.Name + stdout := io.Out() + stdin := io.In() + stderr := io.Err() + rootDir := cfg.rootDir + mode := tests.ImportModeStdlibsOnly testFuncs := &testFuncs{ PackageName: pkgName, @@ -435,63 +416,85 @@ func runTestFiles( } loadTestFuncs(pkgName, testFuncs, files) - // before/after statistics - numPackagesBefore := m.Store.NumMemPackages() - testmain, err := formatTestmain(testFuncs) if err != nil { log.Fatal(err) } - m.RunFiles(files.Files...) - n := gno.MustParseFile("main_test.gno", testmain) - m.RunFiles(n) - for _, test := range testFuncs.Tests { - testFuncStr := fmt.Sprintf("%q", test.Name) + func() { + testStore := tests.TestStore( + rootDir, "", + stdin, stdout, stderr, + mode, + ) + testStore.ClearCache() - eval := m.Eval(gno.Call("runtest", testFuncStr)) + m := tests.TestMachine(testStore, stdout, memPkg.Name) + testFuncStr := fmt.Sprintf("%q", test.Name) - ret := eval[0].GetString() - if ret == "" { - err := errors.New("failed to execute unit test: %q", test.Name) - errs = multierr.Append(errs, err) - io.ErrPrintfln("--- FAIL: %s [internal gno testing error]", test.Name) - continue - } + // before/after statistics + numPackagesBefore := m.Store.NumMemPackages() - // TODO: replace with amino or send native type? - var rep report - err = json.Unmarshal([]byte(ret), &rep) - if err != nil { - errs = multierr.Append(errs, err) - io.ErrPrintfln("--- FAIL: %s [internal gno testing error]", test.Name) - continue - } + defer func() { + if r := recover(); r != nil { + err := fmt.Errorf("panic: %v\nstack:\n%v\ngno machine: %v", r, string(debug.Stack()), m.String()) + errs = multierr.Append(err, errs) + } + }() + m.RunMemPackage(memPkg, true) - if rep.Failed { - err := errors.New("failed: %q", test.Name) - errs = multierr.Append(errs, err) - } + filesCopy := files.CopyFileSet() + //filesCopy := files + //fmt.Printf("AAA: %v\n", files) + //fmt.Printf("BBB: %v\n", filesCopy) + m.RunFiles(filesCopy.Files...) + n := gno.MustParseFile("main_test.gno", testmain) + m.RunFiles(n) + + eval := m.Eval(gno.Call("runtest", testFuncStr)) + + ret := eval[0].GetString() + if ret == "" { + err := errors.New("failed to execute unit test: %q", test.Name) + errs = multierr.Append(errs, err) + io.ErrPrintfln("--- FAIL: %s [internal gno testing error]", test.Name) + return + } - if printRuntimeMetrics { - imports := m.Store.NumMemPackages() - numPackagesBefore - 1 - // XXX: store changes - // XXX: max mem consumption - allocsVal := "n/a" - if m.Alloc != nil { - maxAllocs, allocs := m.Alloc.Status() - allocsVal = fmt.Sprintf("%s(%.2f%%)", - prettySize(allocs), - float64(allocs)/float64(maxAllocs)*100, + // TODO: replace with amino or send native type? + var rep report + err = json.Unmarshal([]byte(ret), &rep) + if err != nil { + errs = multierr.Append(errs, err) + io.ErrPrintfln("--- FAIL: %s [internal gno testing error]", test.Name) + return + } + + if rep.Failed { + err := errors.New("failed: %q", test.Name) + errs = multierr.Append(errs, err) + } + + if printRuntimeMetrics { + imports := m.Store.NumMemPackages() - numPackagesBefore - 1 + // XXX: store changes + // XXX: max mem consumption + allocsVal := "n/a" + if m.Alloc != nil { + maxAllocs, allocs := m.Alloc.Status() + allocsVal = fmt.Sprintf("%s(%.2f%%)", + prettySize(allocs), + float64(allocs)/float64(maxAllocs)*100, + ) + } + io.ErrPrintfln("--- runtime: cycle=%s imports=%d allocs=%s", + prettySize(m.Cycles), + imports, + allocsVal, ) } - io.ErrPrintfln("--- runtime: cycle=%s imports=%d allocs=%s", - prettySize(m.Cycles), - imports, - allocsVal, - ) - } + }() } return errs diff --git a/gnovm/pkg/gnolang/nodes_copy.go b/gnovm/pkg/gnolang/nodes_copy.go index e8d7dbb31ec..a8ece095e5b 100644 --- a/gnovm/pkg/gnolang/nodes_copy.go +++ b/gnovm/pkg/gnolang/nodes_copy.go @@ -362,6 +362,9 @@ func (fs *FileSet) CopyFileSet() *FileSet { func (x *FileNode) Copy() Node { return &FileNode{ + // XXX: Attributes? + // XXX: StaticBlock? + Name: x.Name, PkgName: x.PkgName, Decls: copyDecls(x.Decls), }