Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

bfdemo_test.gno

5.17 Kb · 168 lines
  1package bfdemo
  2
  3import (
  4	"strings"
  5	"testing"
  6
  7	"gno.land/p/nym-moultest123/x/vm/vmkit/v0"
  8	"gno.land/p/nt/avl/v0"
  9	"gno.land/p/nt/seqid/v0"
 10	"gno.land/p/nt/testutils/v0"
 11	"gno.land/p/nt/uassert/v0"
 12)
 13
 14const hello = "++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------."
 15
 16// resetState puts the realm globals back where init() left them. Realm state
 17// persists for the whole test binary, so every test that asserts on ids or on
 18// the rendered instance list has to start from here.
 19func resetState() {
 20	store = vmkit.NewStore()
 21	inputs = avl.NewTree()
 22	idgen = seqid.ID(0)
 23	count = 0
 24}
 25
 26func TestUploadAndRunToCompletion(cur realm, t *testing.T) {
 27	resetState()
 28	alice := testutils.TestAddress("alice")
 29	testing.SetRealm(testing.NewUserRealm(alice))
 30
 31	id := Upload(cross(cur), hello, "", 0)
 32	uassert.Equal(t, "halted", Step(cross(cur), id, 0))
 33
 34	inst := store.Get(id)
 35	uassert.Equal(t, "Hello World", string(inst.Output))
 36	uassert.Equal(t, int64(1), inst.Slices)
 37	uassert.Equal(t, alice, inst.Owner)
 38}
 39
 40// TestOneProgramAcrossManyTransactions is what the realm exists to
 41// demonstrate: the same computation, finished over several calls, each paying
 42// for its own slice.
 43func TestOneProgramAcrossManyTransactions(cur realm, t *testing.T) {
 44	resetState()
 45	testing.SetRealm(testing.NewUserRealm(testutils.TestAddress("alice")))
 46
 47	id := Upload(cross(cur), hello, "", 0)
 48	status := ""
 49	slices := 0
 50	for slices < 500 {
 51		status = Step(cross(cur), id, 7)
 52		slices++
 53		if status != "running" {
 54			break
 55		}
 56	}
 57
 58	inst := store.Get(id)
 59	uassert.Equal(t, "halted", status)
 60	uassert.Equal(t, "Hello World", string(inst.Output))
 61	uassert.True(t, inst.Slices > 1, "took more than one slice")
 62}
 63
 64func TestInputIsReadThroughTheHost(cur realm, t *testing.T) {
 65	resetState()
 66	testing.SetRealm(testing.NewUserRealm(testutils.TestAddress("alice")))
 67
 68	// ",[.,]" echoes its input back.
 69	id := Upload(cross(cur), ",[.,]", "gno.land", 0)
 70	uassert.Equal(t, "halted", Step(cross(cur), id, 0))
 71	uassert.Equal(t, "gno.land", string(store.Get(id).Output))
 72}
 73
 74func TestBudgetStopsAProgram(cur realm, t *testing.T) {
 75	resetState()
 76	testing.SetRealm(testing.NewUserRealm(testutils.TestAddress("alice")))
 77
 78	id := Upload(cross(cur), hello, "", 12)
 79	uassert.Equal(t, "out of fuel", Step(cross(cur), id, 0))
 80	uassert.Equal(t, int64(12), store.Get(id).FuelUsed)
 81
 82	// And it stays stopped rather than quietly continuing.
 83	uassert.AbortsContains(t, cur, "instance is out of fuel", func() {
 84		Step(cross(cur), id, 0)
 85	})
 86}
 87
 88func TestUploadRejectsAMalformedProgram(cur realm, t *testing.T) {
 89	resetState()
 90	testing.SetRealm(testing.NewUserRealm(testutils.TestAddress("alice")))
 91
 92	// Rejected by the transaction that submitted it, not by whoever pays
 93	// for the first slice later.
 94	uassert.AbortsContains(t, cur, "unmatched", func() {
 95		Upload(cross(cur), "+++[", "", 0)
 96	})
 97	uassert.Equal(t, 0, count)
 98}
 99
100func TestRemoveIsOwnerOnly(cur realm, t *testing.T) {
101	resetState()
102	alice := testutils.TestAddress("alice")
103	bob := testutils.TestAddress("bob")
104
105	testing.SetRealm(testing.NewUserRealm(alice))
106	id := Upload(cross(cur), hello, "", 0)
107
108	testing.SetRealm(testing.NewUserRealm(bob))
109	uassert.AbortsContains(t, cur, "not your instance", func() {
110		Remove(cross(cur), id)
111	})
112
113	// Anyone may pay for a slice, though: the program and its budget were
114	// both fixed at upload.
115	uassert.Equal(t, "halted", Step(cross(cur), id, 0))
116
117	testing.SetRealm(testing.NewUserRealm(alice))
118	Remove(cross(cur), id)
119	uassert.Equal(t, 0, count)
120	uassert.True(t, store.Get(id) == nil)
121}
122
123func TestOutputIsCappedAndTrapped(cur realm, t *testing.T) {
124	resetState()
125	testing.SetRealm(testing.NewUserRealm(testutils.TestAddress("alice")))
126
127	// A cell set to 1 and printed forever: an infinite writer.
128	id := Upload(cross(cur), "+[.]", "", 0)
129	status := Step(cross(cur), id, 0)
130
131	inst := store.Get(id)
132	uassert.Equal(t, "trapped", status)
133	uassert.Equal(t, "output limit reached", inst.Trap)
134	uassert.Equal(t, MaxOutput, len(inst.Output))
135}
136
137func TestRenderUnknownInstance(t *testing.T) {
138	resetState()
139	uassert.True(t, strings.Contains(Render("/nope"), "No such instance"))
140}
141
142// TestRenderEscapesGuestOutput pins the rule that a realm cannot be fixed in
143// place once it is live: everything a caller controls is escaped before it
144// reaches the page.
145func TestRenderEscapesGuestOutput(cur realm, t *testing.T) {
146	resetState()
147	testing.SetRealm(testing.NewUserRealm(testutils.TestAddress("alice")))
148
149	// Print a backtick (96), which would otherwise close the code fence.
150	src := strings.Repeat("+", 96) + "."
151	id := Upload(cross(cur), src, "", 0)
152	Step(cross(cur), id, 0)
153
154	page := Render("/" + id)
155	uassert.Equal(t, "`", string(store.Get(id).Output))
156	uassert.True(t, strings.Contains(page, "\\x60"), "the backtick is escaped")
157
158	// The comment text in a program never reaches the page at all: only
159	// the eight operators do.
160	uassert.Equal(t, "+++[-]<>", operatorsOnly("+++ this is a comment [-] </script>"))
161}
162
163// Output sits in a code span, where markdown escapes show verbatim: only the
164// pipe is escaped there, and nothing else.
165func TestCodeCell(t *testing.T) {
166	uassert.Equal(t, "`Hello, gno.land\\n`", codeCell("Hello, gno.land\\n"))
167	uassert.Equal(t, "`a\\|b`", codeCell("a|b"))
168}