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

dev_test.gno

4.49 Kb · 166 lines
  1package dev
  2
  3import (
  4	"chain/runtime"
  5	"strconv"
  6	"strings"
  7	"testing"
  8
  9	"gno.land/p/nt/testutils/v0"
 10	"gno.land/p/nt/uassert/v0"
 11
 12	ninep "gno.land/p/moul/x/plan9/ninep/v0"
 13
 14	nsrealm "gno.land/r/moul/x/plan9/ns/v1"
 15)
 16
 17func read(t *testing.T, name string) string {
 18	t.Helper()
 19	f, err := tree.Root().Walk(name)
 20	if err != nil {
 21		t.Fatalf("walk %s: %v", name, err)
 22	}
 23	data, err := ninep.ReadAll(f)
 24	if err != nil {
 25		t.Fatalf("read %s: %v", name, err)
 26	}
 27	return data
 28}
 29
 30func TestDevicesFollowTheChain(t *testing.T) {
 31	if got, want := read(t, "sysname"), runtime.ChainID(); got != want {
 32		t.Errorf("sysname: got %q, want %q", got, want)
 33	}
 34	if got, want := read(t, "domain"), runtime.ChainDomain(); got != want {
 35		t.Errorf("domain: got %q, want %q", got, want)
 36	}
 37	if got, want := read(t, "height"), strconv.FormatInt(runtime.ChainHeight(), 10); got != want {
 38		t.Errorf("height: got %q, want %q", got, want)
 39	}
 40
 41	// A device is code, not storage: advance the chain and the file follows.
 42	before := read(t, "height")
 43	testing.SkipHeights(5)
 44	after := read(t, "height")
 45	if before == after {
 46		t.Errorf("height did not move: %q", after)
 47	}
 48	b, _ := strconv.ParseInt(before, 10, 64)
 49	a, _ := strconv.ParseInt(after, 10, 64)
 50	if a-b != 5 {
 51		t.Errorf("height moved by %d, want 5", a-b)
 52	}
 53}
 54
 55func TestRandomIsBlockDeterministic(t *testing.T) {
 56	a := read(t, "random")
 57	if len(a) != 64 {
 58		t.Errorf("a sha256 in hex is 64 characters, got %d", len(a))
 59	}
 60	if a != read(t, "random") {
 61		t.Error("two reads in the same block must agree, or Render is a consensus bug")
 62	}
 63	testing.SkipHeights(1)
 64	if a == read(t, "random") {
 65		t.Error("the value should change with the block")
 66	}
 67}
 68
 69func TestNullAndZero(t *testing.T) {
 70	if got := read(t, "null"); got != "" {
 71		t.Errorf("null: %q", got)
 72	}
 73	f, _ := tree.Root().Walk("zero")
 74	got, _ := f.Read(0, 4)
 75	if got != "\x00\x00\x00\x00" {
 76		t.Errorf("zero: %q", got)
 77	}
 78	// An endless file must not answer an unbounded read with an endless value.
 79	if got, _ := f.Read(0, -1); got != "" {
 80		t.Errorf("unbounded read of /dev/zero: %d bytes", len(got))
 81	}
 82	if got, _ := f.Read(0, 1<<20); len(got) != 4096 {
 83		t.Errorf("a huge read should cap at 4096, got %d", len(got))
 84	}
 85}
 86
 87func TestSessionReportsAPlainKey(t *testing.T) {
 88	got := read(t, "session")
 89	if got != "session no\nscope full\n" {
 90		t.Errorf("a plain key has no session scope: %q", got)
 91	}
 92}
 93
 94func TestDriversListsEveryDevice(t *testing.T) {
 95	got := read(t, "drivers")
 96	ents, err := tree.Root().ReadDir()
 97	if err != nil {
 98		t.Fatalf("readdir: %v", err)
 99	}
100	for _, e := range ents {
101		if !strings.Contains(got, e.Name+"\t") {
102			t.Errorf("/dev/drivers does not document %q", e.Name)
103		}
104	}
105	if len(ents) != len(docs) {
106		t.Errorf("%d devices but %d documented", len(ents), len(docs))
107	}
108}
109
110func TestTreeIsReadOnly(t *testing.T) {
111	var f ninep.File = tree.Root()
112	if _, ok := f.(ninep.Mutable); ok {
113		t.Error("a device tree handed to other realms must not be Mutable")
114	}
115}
116
117// TestPostedAtDeployTime is the cross-realm claim the whole suite rests on:
118// this realm pushed an interface value into another realm at init, and that
119// realm can call it back later from a read.
120func TestPostedAtDeployTime(t *testing.T) {
121	found := false
122	for _, s := range nsrealm.Services() {
123		if s == "dev" {
124			found = true
125		}
126	}
127	if !found {
128		t.Fatalf("dev is not posted in /srv: %v", nsrealm.Services())
129	}
130}
131
132func TestAFreshNamespaceHasDevBound(cur realm, t *testing.T) {
133	user := testutils.TestAddress("glenda")
134	testing.SetRealm(testing.NewUserRealm(user))
135	nsrealm.Reset(cross(cur))
136	// Touch the namespace so it is built with /dev already bound.
137	nsrealm.Exec(cross(cur), "echo hi > /tmp/hi")
138
139	out, err := nsrealm.Run(user.String(), "ls /dev")
140	if err != nil {
141		t.Fatalf("ls /dev: %v", err)
142	}
143	for _, d := range docs {
144		if !strings.Contains(out, d[0]) {
145			t.Errorf("ls /dev is missing %q: %q", d[0], out)
146		}
147	}
148	got, err := nsrealm.Run(user.String(), "cat /dev/sysname")
149	if err != nil {
150		t.Fatalf("cat: %v", err)
151	}
152	if got != runtime.ChainID() {
153		t.Errorf("reading this realm's device through another realm's namespace: %q", got)
154	}
155	// And the mount table says where it came from.
156	if ns := nsrealm.Namespace(user.String()); !strings.Contains(ns, "bind /srv/dev /dev") {
157		t.Errorf("mount table: %q", ns)
158	}
159}
160
161// The device name comes from the URL: escaped in the heading.
162func TestDeviceNameIsEscaped(t *testing.T) {
163	// No slash in the payload: Render splits the path on "/".
164	out := Render("cat/x` [claim](evil.example) `")
165	uassert.False(t, strings.Contains(out, "[claim](evil.example)"), out)
166}