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.

riscvdemo_test.gno

7.15 Kb · 225 lines
  1package riscvdemo
  2
  3import (
  4	"strings"
  5	"testing"
  6
  7	"gno.land/p/nym-moultest123/x/vm/riscv/v0"
  8	"gno.land/p/nym-moultest123/x/vm/vmkit/v0"
  9	"gno.land/p/nt/avl/v0"
 10	"gno.land/p/nt/seqid/v0"
 11	"gno.land/p/nt/testutils/v0"
 12	"gno.land/p/nt/uassert/v0"
 13)
 14
 15// resetState puts the realm globals back where init() left them. Realm state
 16// persists for the whole test binary, so every test that asserts on ids or on
 17// the rendered instance list has to start from here.
 18func resetState() {
 19	store = vmkit.NewStore()
 20	inputs = avl.NewTree()
 21	idgen = seqid.ID(0)
 22	count = 0
 23}
 24
 25func alice() address { return testutils.TestAddress("alice") }
 26
 27func TestUploadAndRunToCompletion(cur realm, t *testing.T) {
 28	resetState()
 29	testing.SetRealm(testing.NewUserRealm(alice()))
 30
 31	id := UploadSample(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, gno.land\n", string(inst.Output))
 36	uassert.Equal(t, int64(1), inst.Slices)
 37	uassert.Equal(t, alice(), inst.Owner)
 38	uassert.Equal(t, "riscv32im", inst.VM)
 39}
 40
 41// Every sample on the front page has to reach its documented answer, or the
 42// page offers a button that traps when pressed. The words are assembled
 43// outside this repo, so this is the test that catches a mistyped one.
 44func TestEverySampleReachesItsAnswer(cur realm, t *testing.T) {
 45	resetState()
 46	testing.SetRealm(testing.NewUserRealm(alice()))
 47
 48	cases := []struct {
 49		slug   string
 50		reg    int
 51		want   uint32
 52		output string
 53	}{
 54		{"hello", 0, 0, "Hello, gno.land\n"},
 55		{"sum100", 5, 5050, ""},
 56		{"heavy", 5, 1250025000, ""},
 57		{"mext", 5, 3000000021, ""},
 58		{"clang", 0, 0, "fb7ffba0"},
 59		{"ledger", 0, 0, "alice 70\nbob 80\n"},
 60	}
 61	for _, c := range cases {
 62		id := UploadSample(cross(cur), c.slug, sampleByName(c.slug).input, 0)
 63		status := ""
 64		for i := 0; i < 20; i++ {
 65			status = Step(cross(cur), id, 0)
 66			if status != "running" {
 67				break
 68			}
 69		}
 70		uassert.Equal(t, "halted", status, c.slug+" must halt")
 71		inst := store.Get(id)
 72		uassert.Equal(t, c.output, string(inst.Output), c.slug+" output")
 73		if c.reg != 0 {
 74			var m riscv.Machine
 75			uassert.NoError(t, m.Restore(inst.Snapshot))
 76			uassert.Equal(t, uint64(c.want), uint64(m.Registers()[c.reg]), c.slug+" result")
 77		}
 78	}
 79}
 80
 81// TestOneProgramAcrossManyTransactions is what the realm exists to
 82// demonstrate: the same computation, finished over several calls, each paying
 83// for its own slice.
 84func TestOneProgramAcrossManyTransactions(cur realm, t *testing.T) {
 85	resetState()
 86	testing.SetRealm(testing.NewUserRealm(alice()))
 87
 88	id := UploadSample(cross(cur), "heavy", "", 0)
 89	status := ""
 90	for i := 0; i < 50; i++ {
 91		status = Step(cross(cur), id, 80000)
 92		if status != "running" {
 93			break
 94		}
 95	}
 96	inst := store.Get(id)
 97	uassert.Equal(t, "halted", status)
 98	uassert.True(t, inst.Slices > 1, "a 200k instruction program must not finish in one slice")
 99	uassert.Equal(t, int64(200006), inst.FuelUsed)
100}
101
102func TestUploadTakesHex(cur realm, t *testing.T) {
103	resetState()
104	testing.SetRealm(testing.NewUserRealm(alice()))
105
106	// addi a7, zero, 93 ; ecall
107	id := Upload(cross(cur), "0x9308d005 73000000", "", 0)
108	_ = id
109}
110
111func TestUploadRejectsBadImages(cur realm, t *testing.T) {
112	resetState()
113	testing.SetRealm(testing.NewUserRealm(alice()))
114
115	// Called without cross(): a panic raised through a crossing call cannot be
116	// recovered, so uassert would never see it.
117	uassert.PanicsWithMessage(t, cur, "riscvdemo: image is not valid hex", func() {
118		Upload(cur, "zzzz", "", 0)
119	})
120	uassert.PanicsWithMessage(t, cur, "riscvdemo: image is not valid hex", func() {
121		Upload(cur, "abc", "", 0)
122	})
123	uassert.PanicsWithMessage(t, cur, "riscvdemo: no such sample", func() {
124		UploadSample(cur, "nope", "", 0)
125	})
126	uassert.PanicsWithMessage(t, cur,
127		"riscvdemo: riscv: image length is not a whole number of instructions",
128		func() { Upload(cur, "0011", "", 0) })
129}
130
131func TestDecodeHex(t *testing.T) {
132	b, ok := decodeHex("0xDEADbeef")
133	uassert.True(t, ok)
134	uassert.Equal(t, 4, len(b))
135	uassert.Equal(t, uint64(0xDE), uint64(b[0]))
136	uassert.Equal(t, uint64(0xEF), uint64(b[3]))
137
138	_, ok = decodeHex("")
139	uassert.False(t, ok)
140	_, ok = decodeHex("0x")
141	uassert.False(t, ok)
142	// Whitespace is skipped, because a pasted hexdump has it.
143	b, ok = decodeHex("00 11\n22\t33")
144	uassert.True(t, ok)
145	uassert.Equal(t, 4, len(b))
146	// An odd number of digits is still a refusal, whitespace or not.
147	_, ok = decodeHex("00 1")
148	uassert.False(t, ok)
149}
150
151func TestRenderHomeOffersEverySample(cur realm, t *testing.T) {
152	resetState()
153	out := Render("")
154	uassert.True(t, strings.Contains(out, "RV32IM, on chain"), "the title")
155	for _, s := range samples {
156		uassert.True(t, strings.Contains(out, s.name), "home must offer "+s.slug)
157	}
158	uassert.True(t, strings.Contains(out, "No instances yet"), "the empty state")
159}
160
161// The register file is the interesting half of this VM: a program that writes
162// nothing still computed something, and the snapshot is the only place it is
163// legible between transactions.
164func TestRenderInstanceShowsTheRegisterFile(cur realm, t *testing.T) {
165	resetState()
166	testing.SetRealm(testing.NewUserRealm(alice()))
167
168	id := UploadSample(cross(cur), "sum100", "", 0)
169	Step(cross(cur), id, 0)
170
171	out := Render("/" + id)
172	uassert.True(t, strings.Contains(out, "## Registers"), "the section")
173	uassert.True(t, strings.Contains(out, "5050"), "t0 must show the answer")
174	uassert.True(t, strings.Contains(out, "halted"), "the status")
175}
176
177// The instance page is all hex: the pc, every register, the image dump.
178// gno.land/p/nt/ufmt/v0 has no %X and no width flags, so this page shipped
179// printing "0x(unhandled verb: %X)" in every cell; the test pins the fix.
180func TestRenderInstanceHex(cur realm, t *testing.T) {
181	resetState()
182	testing.SetRealm(testing.NewUserRealm(alice()))
183
184	// addi a7, zero, 93 ; ecall (exit)
185	id := Upload(cross(cur), "0x9308d005 73000000", "", 0)
186	Step(cross(cur), id, 0)
187
188	out := Render("/" + id)
189	uassert.False(t, strings.Contains(out, "unhandled"), out)
190	uassert.True(t, strings.Contains(out, "| a7 | 0x0000005D | 93 | 93 |"), out)
191	uassert.True(t, strings.Contains(out, "05d00893 00000073"), out)
192}
193
194// The instance list shows output in a code span, where markdown escapes do
195// not apply: ui.Cell's backslashes used to reach the reader as
196// `Hello, gno\.land\\n`. Only the pipe needs escaping inside the span.
197func TestRenderOutputCell(cur realm, t *testing.T) {
198	resetState()
199	testing.SetRealm(testing.NewUserRealm(alice()))
200
201	id := UploadSample(cross(cur), "hello", "", 0)
202	Step(cross(cur), id, 0)
203
204	out := Render("")
205	uassert.True(t, strings.Contains(out, "| `Hello, gno.land\\n` |"), out)
206	uassert.Equal(t, "`a\\|b`", codeCell("a|b"))
207}
208
209func TestRenderUnknownInstance(t *testing.T) {
210	uassert.True(t, strings.Contains(Render("/nope"), "No such instance"))
211}
212
213// ExampleRender pins the realm's output for a path whose content cannot drift
214// with the instance list. The home page and the instance page both produce
215// consecutive blank lines, which an example block cannot represent, so they
216// are asserted in the tests above instead.
217func ExampleRender() {
218	print(Render("/missing"))
219	// Output:
220	// # Instance missing
221	//
222	// [← all instances](/r/nym-moultest123/x/vm/riscvdemo/v0)
223	//
224	// **No such instance.** It was never uploaded, or its owner removed it.
225}