All checks were successful
		
		
	
	Test / Create distribution (push) Successful in 25s
				
			Test / Sandbox (push) Successful in 39s
				
			Test / Sandbox (race detector) (push) Successful in 39s
				
			Test / Hakurei (push) Successful in 42s
				
			Test / Hpkg (push) Successful in 40s
				
			Test / Hakurei (race detector) (push) Successful in 44s
				
			Test / Flake checks (push) Successful in 1m23s
				
			Sharing and persisting these directories do not always make sense. Make it optional here. Closes #16. Signed-off-by: Ophestra <cat@gensokyo.uk>
		
			
				
	
	
		
			278 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			278 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package hst_test
 | |
| 
 | |
| import (
 | |
| 	"encoding/json"
 | |
| 	"errors"
 | |
| 	"net"
 | |
| 	"os"
 | |
| 	"reflect"
 | |
| 	"syscall"
 | |
| 	"testing"
 | |
| 
 | |
| 	"hakurei.app/container/stub"
 | |
| 	"hakurei.app/hst"
 | |
| 	"hakurei.app/message"
 | |
| )
 | |
| 
 | |
| func TestAppError(t *testing.T) {
 | |
| 	t.Parallel()
 | |
| 
 | |
| 	testCases := []struct {
 | |
| 		name    string
 | |
| 		err     error
 | |
| 		s       string
 | |
| 		message string
 | |
| 		is, isF error
 | |
| 	}{
 | |
| 		{"message", &hst.AppError{Step: "obtain uid from hsu", Err: stub.UniqueError(0),
 | |
| 			Msg: "the setuid helper is missing: /run/wrappers/bin/hsu"},
 | |
| 			"unique error 0 injected by the test suite",
 | |
| 			"the setuid helper is missing: /run/wrappers/bin/hsu",
 | |
| 			stub.UniqueError(0), os.ErrNotExist},
 | |
| 
 | |
| 		{"os.PathError", &hst.AppError{Step: "passthrough os.PathError",
 | |
| 			Err: &os.PathError{Op: "stat", Path: "/proc/nonexistent", Err: os.ErrNotExist}},
 | |
| 			"stat /proc/nonexistent: file does not exist",
 | |
| 			"cannot stat /proc/nonexistent: file does not exist",
 | |
| 			os.ErrNotExist, stub.UniqueError(0xdeadbeef)},
 | |
| 
 | |
| 		{"os.LinkError", &hst.AppError{Step: "passthrough os.LinkError",
 | |
| 			Err: &os.LinkError{Op: "link", Old: "/proc/self", New: "/proc/nonexistent", Err: os.ErrNotExist}},
 | |
| 			"link /proc/self /proc/nonexistent: file does not exist",
 | |
| 			"cannot link /proc/self /proc/nonexistent: file does not exist",
 | |
| 			os.ErrNotExist, stub.UniqueError(0xdeadbeef)},
 | |
| 
 | |
| 		{"os.SyscallError", &hst.AppError{Step: "passthrough os.SyscallError",
 | |
| 			Err: &os.SyscallError{Syscall: "meow", Err: syscall.ENOSYS}},
 | |
| 			"meow: function not implemented",
 | |
| 			"cannot meow: function not implemented",
 | |
| 			syscall.ENOSYS, syscall.ENOTRECOVERABLE},
 | |
| 
 | |
| 		{"net.OpError", &hst.AppError{Step: "passthrough net.OpError",
 | |
| 			Err: &net.OpError{Op: "dial", Net: "cat", Err: net.UnknownNetworkError("cat")}},
 | |
| 			"dial cat: unknown network cat",
 | |
| 			"cannot dial cat: unknown network cat",
 | |
| 			net.UnknownNetworkError("cat"), syscall.ENOTRECOVERABLE},
 | |
| 
 | |
| 		{"default", &hst.AppError{Step: "initialise container configuration", Err: stub.UniqueError(1)},
 | |
| 			"unique error 1 injected by the test suite",
 | |
| 			"cannot initialise container configuration: unique error 1 injected by the test suite",
 | |
| 			stub.UniqueError(1), os.ErrInvalid},
 | |
| 	}
 | |
| 	for _, tc := range testCases {
 | |
| 		t.Run(tc.name, func(t *testing.T) {
 | |
| 			t.Parallel()
 | |
| 
 | |
| 			t.Run("error", func(t *testing.T) {
 | |
| 				t.Parallel()
 | |
| 				if got := tc.err.Error(); got != tc.s {
 | |
| 					t.Errorf("Error: %s, want %s", got, tc.s)
 | |
| 				}
 | |
| 			})
 | |
| 
 | |
| 			t.Run("message", func(t *testing.T) {
 | |
| 				t.Parallel()
 | |
| 				gotMessage, gotMessageOk := message.GetMessage(tc.err)
 | |
| 				if want := tc.message != "\x00"; gotMessageOk != want {
 | |
| 					t.Errorf("GetMessage: ok = %v, want %v", gotMessage, want)
 | |
| 				}
 | |
| 
 | |
| 				if gotMessageOk {
 | |
| 					if gotMessage != tc.message {
 | |
| 						t.Errorf("GetMessage: %s, want %s", gotMessage, tc.message)
 | |
| 					}
 | |
| 				}
 | |
| 			})
 | |
| 
 | |
| 			t.Run("is", func(t *testing.T) {
 | |
| 				t.Parallel()
 | |
| 				if !errors.Is(tc.err, tc.is) {
 | |
| 					t.Errorf("Is: unexpected false for %v", tc.is)
 | |
| 				}
 | |
| 				if errors.Is(tc.err, tc.isF) {
 | |
| 					t.Errorf("Is: unexpected true for %v", tc.isF)
 | |
| 				}
 | |
| 			})
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestTemplate(t *testing.T) {
 | |
| 	t.Parallel()
 | |
| 
 | |
| 	const want = `{
 | |
| 	"id": "org.chromium.Chromium",
 | |
| 	"enablements": {
 | |
| 		"wayland": true,
 | |
| 		"dbus": true,
 | |
| 		"pulse": true
 | |
| 	},
 | |
| 	"session_bus": {
 | |
| 		"see": null,
 | |
| 		"talk": [
 | |
| 			"org.freedesktop.Notifications",
 | |
| 			"org.freedesktop.FileManager1",
 | |
| 			"org.freedesktop.ScreenSaver",
 | |
| 			"org.freedesktop.secrets",
 | |
| 			"org.kde.kwalletd5",
 | |
| 			"org.kde.kwalletd6",
 | |
| 			"org.gnome.SessionManager"
 | |
| 		],
 | |
| 		"own": [
 | |
| 			"org.chromium.Chromium.*",
 | |
| 			"org.mpris.MediaPlayer2.org.chromium.Chromium.*",
 | |
| 			"org.mpris.MediaPlayer2.chromium.*"
 | |
| 		],
 | |
| 		"call": {
 | |
| 			"org.freedesktop.portal.*": "*"
 | |
| 		},
 | |
| 		"broadcast": {
 | |
| 			"org.freedesktop.portal.*": "@/org/freedesktop/portal/*"
 | |
| 		},
 | |
| 		"filter": true
 | |
| 	},
 | |
| 	"system_bus": {
 | |
| 		"see": null,
 | |
| 		"talk": [
 | |
| 			"org.bluez",
 | |
| 			"org.freedesktop.Avahi",
 | |
| 			"org.freedesktop.UPower"
 | |
| 		],
 | |
| 		"own": null,
 | |
| 		"call": null,
 | |
| 		"broadcast": null,
 | |
| 		"filter": true
 | |
| 	},
 | |
| 	"extra_perms": [
 | |
| 		{
 | |
| 			"ensure": true,
 | |
| 			"path": "/var/lib/hakurei/u0",
 | |
| 			"x": true
 | |
| 		},
 | |
| 		{
 | |
| 			"path": "/var/lib/hakurei/u0/org.chromium.Chromium",
 | |
| 			"r": true,
 | |
| 			"w": true,
 | |
| 			"x": true
 | |
| 		}
 | |
| 	],
 | |
| 	"identity": 9,
 | |
| 	"groups": [
 | |
| 		"video",
 | |
| 		"dialout",
 | |
| 		"plugdev"
 | |
| 	],
 | |
| 	"container": {
 | |
| 		"hostname": "localhost",
 | |
| 		"wait_delay": -1,
 | |
| 		"env": {
 | |
| 			"GOOGLE_API_KEY": "AIzaSyBHDrl33hwRp4rMQY0ziRbj8K9LPA6vUCY",
 | |
| 			"GOOGLE_DEFAULT_CLIENT_ID": "77185425430.apps.googleusercontent.com",
 | |
| 			"GOOGLE_DEFAULT_CLIENT_SECRET": "OTJgUOQcT7lO7GsGZq2G4IlT"
 | |
| 		},
 | |
| 		"filesystem": [
 | |
| 			{
 | |
| 				"type": "bind",
 | |
| 				"dst": "/",
 | |
| 				"src": "/var/lib/hakurei/base/org.debian",
 | |
| 				"write": true,
 | |
| 				"special": true
 | |
| 			},
 | |
| 			{
 | |
| 				"type": "bind",
 | |
| 				"dst": "/etc/",
 | |
| 				"src": "/etc/",
 | |
| 				"special": true
 | |
| 			},
 | |
| 			{
 | |
| 				"type": "ephemeral",
 | |
| 				"dst": "/tmp/",
 | |
| 				"write": true,
 | |
| 				"perm": 493
 | |
| 			},
 | |
| 			{
 | |
| 				"type": "overlay",
 | |
| 				"dst": "/nix/store",
 | |
| 				"lower": [
 | |
| 					"/var/lib/hakurei/base/org.nixos/ro-store"
 | |
| 				],
 | |
| 				"upper": "/var/lib/hakurei/nix/u0/org.chromium.Chromium/rw-store/upper",
 | |
| 				"work": "/var/lib/hakurei/nix/u0/org.chromium.Chromium/rw-store/work"
 | |
| 			},
 | |
| 			{
 | |
| 				"type": "link",
 | |
| 				"dst": "/run/current-system",
 | |
| 				"linkname": "/run/current-system",
 | |
| 				"dereference": true
 | |
| 			},
 | |
| 			{
 | |
| 				"type": "link",
 | |
| 				"dst": "/run/opengl-driver",
 | |
| 				"linkname": "/run/opengl-driver",
 | |
| 				"dereference": true
 | |
| 			},
 | |
| 			{
 | |
| 				"type": "bind",
 | |
| 				"dst": "/data/data/org.chromium.Chromium",
 | |
| 				"src": "/var/lib/hakurei/u0/org.chromium.Chromium",
 | |
| 				"write": true,
 | |
| 				"ensure": true
 | |
| 			},
 | |
| 			{
 | |
| 				"type": "bind",
 | |
| 				"src": "/dev/dri",
 | |
| 				"dev": true,
 | |
| 				"optional": true
 | |
| 			}
 | |
| 		],
 | |
| 		"username": "chronos",
 | |
| 		"shell": "/run/current-system/sw/bin/zsh",
 | |
| 		"home": "/data/data/org.chromium.Chromium",
 | |
| 		"path": "/run/current-system/sw/bin/chromium",
 | |
| 		"args": [
 | |
| 			"chromium",
 | |
| 			"--ignore-gpu-blocklist",
 | |
| 			"--disable-smooth-scrolling",
 | |
| 			"--enable-features=UseOzonePlatform",
 | |
| 			"--ozone-platform=wayland"
 | |
| 		],
 | |
| 		"seccomp_compat": true,
 | |
| 		"devel": true,
 | |
| 		"userns": true,
 | |
| 		"host_net": true,
 | |
| 		"host_abstract": true,
 | |
| 		"tty": true,
 | |
| 		"multiarch": true,
 | |
| 		"map_real_uid": true,
 | |
| 		"device": true,
 | |
| 		"share_runtime": true,
 | |
| 		"share_tmpdir": true
 | |
| 	}
 | |
| }`
 | |
| 
 | |
| 	t.Run("marshal", func(t *testing.T) {
 | |
| 		t.Parallel()
 | |
| 		if p, err := json.MarshalIndent(hst.Template(), "", "\t"); err != nil {
 | |
| 			t.Fatalf("cannot marshal: %v", err)
 | |
| 		} else if s := string(p); s != want {
 | |
| 			t.Fatalf("Template:\n%s\nwant:\n%s",
 | |
| 				s, want)
 | |
| 		}
 | |
| 	})
 | |
| 
 | |
| 	t.Run("unmarshal", func(t *testing.T) {
 | |
| 		t.Parallel()
 | |
| 
 | |
| 		var got *hst.Config
 | |
| 		if err := json.Unmarshal([]byte(want), &got); err != nil {
 | |
| 			t.Fatalf("Unmarshal: error = %v", err)
 | |
| 		}
 | |
| 
 | |
| 		wantVal := hst.Template()
 | |
| 		wantVal.Container.Flags = hst.FAll
 | |
| 		if !reflect.DeepEqual(got, wantVal) {
 | |
| 			t.Fatalf("Unmarshal: %#v, want %#v", got, wantVal)
 | |
| 		}
 | |
| 	})
 | |
| }
 |