PathrockNetwork Gno Explorer
HomeBlocksTransactionsTokensRealmsPackagesValidatorsAnalytics

PathrockNetwork Gno Explorer — an independent explorer for Gno.land Mainnet (gnoland-1), operated by PathrockNetwork. Not an official Gno.land service.

gnowebarchive RPC

gno.land/p/moul/x/plan9/rc/v0

Package
Open in gnoweb ↗

Overview

Kind
Pure package
Name
v0
Namespace
moul / x / plan9 / rc
Files
4 (README)(gnomod.toml)
Exported functions
n/a — not supported for pure packages by the node (vm/qfuncs)
Module
gno.land/p/moul/x/plan9/rc/v0
gno
0.9

Files (4)

  • README.mdmarkdown
  • gnomod.tomltoml
  • rc.gnogno
  • rc_test.gnogno
rc_test.gnogno
1package rc23import (4	"strings"5	"testing"67	memfs "gno.land/p/moul/x/plan9/memfs/v0"8	ninep "gno.land/p/moul/x/plan9/ninep/v0"9	ns "gno.land/p/moul/x/plan9/ns/v0"10	synfs "gno.land/p/moul/x/plan9/synfs/v0"11)1213func clock(h int64) func() int64 { return func() int64 { return h } }1415func shell(t *testing.T, files ...string) (*Shell, *memfs.FS) {16	t.Helper()17	sh, fs := NewMemShell("glenda", ReadWrite, clock(100))18	for _, f := range files {19		if err := fs.WriteFile(f, "contents of "+f+"\n", 100); err != nil {20			t.Fatalf("seed %s: %v", f, err)21		}22	}23	return sh, fs24}2526func run(t *testing.T, sh *Shell, line string) string {27	t.Helper()28	out, err := sh.Run(line)29	if err != nil {30		t.Fatalf("%s: %v", line, err)31	}32	return out33}3435func TestTokenize(t *testing.T) {36	tests := []struct {37		name string38		in   string39		want []string40	}{41		{"plain", "ls -l /dev", []string{"ls", "-l", "/dev"}},42		{"extra spaces", "  ls   /dev  ", []string{"ls", "/dev"}},43		{"quoted", "echo 'hello world'", []string{"echo", "hello world"}},44		{"quote inside quote", "echo 'it''s'", []string{"echo", "it's"}},45		{"empty quoted word", "echo ''", []string{"echo", ""}},46		{"adjacent", "echo a'b c'd", []string{"echo", "ab cd"}},47		{"empty line", "", []string{}},48	}49	for _, tt := range tests {50		got, err := tokenize(tt.in)51		if err != nil {52			t.Errorf("%s: %v", tt.name, err)53			continue54		}55		if len(got) != len(tt.want) {56			t.Errorf("%s: got %v, want %v", tt.name, got, tt.want)57			continue58		}59		for i := range got {60			if got[i] != tt.want[i] {61				t.Errorf("%s: token %d got %q, want %q", tt.name, i, got[i], tt.want[i])62			}63		}64	}65	if _, err := tokenize("echo 'unterminated"); err != ErrQuote {66		t.Errorf("unterminated quote: got %v", err)67	}68}6970func TestSplitCommands(t *testing.T) {71	got, err := splitCommands("pwd; ls /\necho 'a;b'")72	if err != nil {73		t.Fatalf("%v", err)74	}75	want := []string{"pwd", " ls /", "echo 'a;b'"}76	if len(got) != len(want) {77		t.Fatalf("got %v", got)78	}79	for i := range want {80		if got[i] != want[i] {81			t.Errorf("%d: got %q, want %q", i, got[i], want[i])82		}83	}84}8586func TestPwdAndCd(t *testing.T) {87	sh, _ := shell(t, "/usr/glenda/lib/profile")88	if got := run(t, sh, "pwd"); got != "/\n" {89		t.Errorf("pwd: %q", got)90	}91	run(t, sh, "cd /usr/glenda")92	if got := run(t, sh, "pwd"); got != "/usr/glenda\n" {93		t.Errorf("pwd after cd: %q", got)94	}95	if got := run(t, sh, "cat lib/profile"); got != "contents of /usr/glenda/lib/profile\n" {96		t.Errorf("relative cat: %q", got)97	}98	run(t, sh, "cd")99	if got := run(t, sh, "pwd"); got != "/\n" {100		t.Errorf("bare cd should go to the root: %q", got)101	}102}103104func TestLs(t *testing.T) {105	sh, _ := shell(t, "/bin/ls", "/bin/rc", "/tmp/x")106	if got := run(t, sh, "ls /"); got != "bin/\ntmp/\n" {107		t.Errorf("ls /: %q", got)108	}109	if got := run(t, sh, "ls /bin"); got != "ls\nrc\n" {110		t.Errorf("ls /bin: %q", got)111	}112	long := run(t, sh, "ls -l /bin")113	if !strings.Contains(long, "-rw-r--r-- glenda") {114		t.Errorf("ls -l: %q", long)115	}116	if got := run(t, sh, "ls /bin/ls"); got != "ls\n" {117		t.Errorf("ls of a file: %q", got)118	}119	if _, err := sh.Run("ls -Z"); err == nil {120		t.Error("an unknown flag should fail")121	}122}123124func TestLsUnionAndUnique(t *testing.T) {125	sh, fs := shell(t)126	fs.WriteFile("/bin/ls", "system\n", 100)127	fs.WriteFile("/usr/glenda/bin/ls", "mine\n", 100)128	run(t, sh, "bind -b /usr/glenda/bin /bin")129130	if got := run(t, sh, "ls /bin"); got != "ls\nls\n" {131		t.Errorf("a union lists both, as Plan 9 does: %q", got)132	}133	if got := run(t, sh, "ls -u /bin"); got != "ls\n" {134		t.Errorf("ls -u: %q", got)135	}136	if got := run(t, sh, "cat /bin/ls"); got != "mine\n" {137		t.Errorf("the member bound before wins the walk: %q", got)138	}139}140141func TestCatErrors(t *testing.T) {142	sh, _ := shell(t, "/bin/ls")143	if _, err := sh.Run("cat /bin"); err == nil {144		t.Error("cat of a directory should fail")145	}146	if _, err := sh.Run("cat /absent"); err == nil {147		t.Error("cat of a missing file should fail")148	}149	if _, err := sh.Run("cat"); err == nil {150		t.Error("cat with no argument should fail")151	}152}153154func TestEchoAndRedirect(t *testing.T) {155	sh, _ := shell(t)156	if got := run(t, sh, "echo hello world"); got != "hello world\n" {157		t.Errorf("echo: %q", got)158	}159	if got := run(t, sh, "echo -n hello"); got != "hello" {160		t.Errorf("echo -n: %q", got)161	}162	run(t, sh, "echo hello > /greeting")163	if got := run(t, sh, "cat /greeting"); got != "hello\n" {164		t.Errorf("after redirect: %q", got)165	}166	run(t, sh, "echo again >> /greeting")167	if got := run(t, sh, "cat /greeting"); got != "hello\nagain\n" {168		t.Errorf("after append: %q", got)169	}170	run(t, sh, "echo replaced > /greeting")171	if got := run(t, sh, "cat /greeting"); got != "replaced\n" {172		t.Errorf("a plain redirect truncates: %q", got)173	}174	if _, err := sh.Run("echo hi >"); err == nil {175		t.Error("a redirect with no target should fail")176	}177}178179func TestMkdirAndRm(t *testing.T) {180	sh, _ := shell(t)181	run(t, sh, "mkdir /tmp")182	run(t, sh, "echo x > /tmp/f")183	if got := run(t, sh, "ls /tmp"); got != "f\n" {184		t.Errorf("ls /tmp: %q", got)185	}186	if _, err := sh.Run("rm /tmp"); err == nil || err.Error() != "rm: "+ninep.ErrNotEmpty.Error() {187		t.Errorf("rm of a non-empty directory: got %v", err)188	}189	run(t, sh, "rm /tmp/f")190	run(t, sh, "rm /tmp")191	if got := run(t, sh, "ls /"); got != "" {192		t.Errorf("root should be empty: %q", got)193	}194	if _, err := sh.Run("rm /absent"); err == nil {195		t.Error("rm of a missing file should fail")196	}197}198199func TestBindAndNs(t *testing.T) {200	sh, _ := shell(t, "/bin/ls", "/usr/glenda/bin/rc")201	run(t, sh, "bind -a /usr/glenda/bin /bin")202	if got := run(t, sh, "ls /bin"); got != "ls\nrc\n" {203		t.Errorf("ls after bind: %q", got)204	}205	want := "bind -a /usr/glenda/bin /bin\ncd /\n"206	if got := run(t, sh, "ns"); got != want {207		t.Errorf("ns:\ngot  %q\nwant %q", got, want)208	}209	run(t, sh, "unmount /usr/glenda/bin /bin")210	if got := run(t, sh, "ls /bin"); got != "ls\n" {211		t.Errorf("after unmount: %q", got)212	}213	if got := run(t, sh, "ns"); got != "cd /\n" {214		t.Errorf("ns after unmount: %q", got)215	}216}217218func TestMountIsASynonym(t *testing.T) {219	sh, _ := shell(t, "/bin/ls", "/srv/dev/height")220	run(t, sh, "mkdir /dev")221	run(t, sh, "mount /srv/dev /dev")222	if got := run(t, sh, "ls /dev"); got != "height\n" {223		t.Errorf("ls /dev: %q", got)224	}225	if !strings.HasPrefix(run(t, sh, "ns"), "mount /srv/dev /dev\n") {226		t.Errorf("ns should record the verb used: %q", run(t, sh, "ns"))227	}228}229230func TestCreateInAUnionNeedsTheFlag(t *testing.T) {231	sh, _ := shell(t, "/bin/ls", "/usr/glenda/bin/rc")232	run(t, sh, "bind -a /usr/glenda/bin /bin")233	if _, err := sh.Run("echo x > /bin/new"); err == nil {234		t.Error("creating in a union with no -c should be refused")235	}236	run(t, sh, "bind -ac /usr/glenda/bin /bin")237	run(t, sh, "echo x > /bin/new")238	if got := run(t, sh, "cat /usr/glenda/bin/new"); got != "x\n" {239		t.Errorf("the create should land in the -c member: %q", got)240	}241}242243func TestReadOnlyModeRefusesWrites(t *testing.T) {244	sh, fs := NewMemShell("glenda", ReadOnly, clock(1))245	fs.WriteFile("/bin/ls", "x\n", 1)246247	// Reads still work.248	if got := run(t, sh, "ls /bin"); got != "ls\n" {249		t.Errorf("ls: %q", got)250	}251	if got := run(t, sh, "cat /bin/ls"); got != "x\n" {252		t.Errorf("cat: %q", got)253	}254	if got := run(t, sh, "echo hi"); got != "hi\n" {255		t.Errorf("echo without a redirect is not a write: %q", got)256	}257	for _, line := range []string{258		"mkdir /tmp",259		"rm /bin/ls",260		"bind /bin /bin",261		"unmount /bin",262		"echo hi > /f",263	} {264		_, err := sh.Run(line)265		if err == nil {266			t.Errorf("%q should be refused in ReadOnly mode", line)267			continue268		}269		if !strings.Contains(err.Error(), ErrReadOnly.Error()) {270			t.Errorf("%q: got %v, want a read-only error", line, err)271		}272	}273}274275func TestStopsAtTheFirstError(t *testing.T) {276	sh, _ := shell(t)277	out, err := sh.Run("echo one; cat /absent; echo three")278	if err == nil {279		t.Fatal("expected an error")280	}281	if out != "one\n" {282		t.Errorf("output should stop at the failure: %q", out)283	}284	if !strings.HasPrefix(err.Error(), "cat: ") {285		t.Errorf("the error should name the command: %v", err)286	}287}288289func TestUnknownCommand(t *testing.T) {290	sh, _ := shell(t)291	if _, err := sh.Run("nosuchthing"); err == nil {292		t.Error("expected an error")293	}294}295296func TestStatAndWalkShowTheUnion(t *testing.T) {297	sh, fs := shell(t)298	fs.WriteFile("/bin/ls", "a\n", 100)299	fs.WriteFile("/usr/glenda/bin/rc", "b\n", 100)300	run(t, sh, "bind -a /usr/glenda/bin /bin")301302	st := run(t, sh, "stat /bin")303	if !strings.Contains(st, "union=2") {304		t.Errorf("stat should report the union width: %q", st)305	}306	w := run(t, sh, "walk /bin/rc")307	lines := strings.Split(strings.TrimSpace(w), "\n")308	if len(lines) != 3 {309		t.Fatalf("walk should report /, /bin and /bin/rc: %q", w)310	}311	if !strings.Contains(lines[1], "union=2") {312		t.Errorf("the union should appear at /bin: %q", lines[1])313	}314	if !strings.Contains(lines[2], "union=1") {315		t.Errorf("a union is top level only, /bin/rc is not one: %q", lines[2])316	}317}318319func TestReadOnlyServerMountedInAWritableNamespace(t *testing.T) {320	// This is the cross-realm shape: a synthetic, read-only tree grafted into321	// a namespace whose root is writable. Reads work; writes are refused322	// where the server is read-only and still allowed elsewhere.323	sh, fs := shell(t)324	fs.MkdirAll("/dev", 100)325	tr := synfs.New("dev", "sys", clock(100))326	tr.Root().Add("height", func() string { return "100" })327	if err := sh.Ns().Mount(tr.Root(), "#dev", "/dev", ns.MREPL); err != nil {328		t.Fatalf("mount: %v", err)329	}330331	if got := run(t, sh, "cat /dev/height"); got != "100" {332		t.Errorf("read through the mount: %q", got)333	}334	if got := run(t, sh, "ls /dev"); got != "height\n" {335		t.Errorf("ls /dev: %q", got)336	}337	if _, err := sh.Run("echo x > /dev/height"); err == nil {338		t.Error("writing to a read-only server should be refused")339	}340	if _, err := sh.Run("rm /dev/height"); err == nil || err.Error() != "rm: "+ninep.ErrReadOnly.Error() {341		t.Errorf("rm on a read-only server: got %v", err)342	}343	// The rest of the namespace is unaffected.344	run(t, sh, "echo fine > /ok")345	if got := run(t, sh, "cat /ok"); got != "fine\n" {346		t.Errorf("got %q", got)347	}348}349350func TestHelpListsEveryCommand(t *testing.T) {351	sh, _ := shell(t)352	out := run(t, sh, "help")353	for _, cmd := range []string{"bind", "cat", "cd", "echo", "ls", "mkdir", "mount", "ns", "pwd", "rm", "stat", "unmount", "walk"} {354		if !strings.Contains(out, cmd) {355			t.Errorf("help does not mention %q", cmd)356		}357	}358}359

Functions

not supported for pure packages by the node (vm/qfuncs)

Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.