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