container/stub: export stub helpers
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				Test / Create distribution (push) Successful in 33s
				
			
		
			
				
	
				Test / Sandbox (push) Successful in 1m53s
				
			
		
			
				
	
				Test / Hakurei (push) Successful in 3m18s
				
			
		
			
				
	
				Test / Sandbox (race detector) (push) Successful in 3m40s
				
			
		
			
				
	
				Test / Hpkg (push) Successful in 3m35s
				
			
		
			
				
	
				Test / Hakurei (race detector) (push) Successful in 5m19s
				
			
		
			
				
	
				Test / Flake checks (push) Successful in 1m39s
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	Test / Create distribution (push) Successful in 33s
				
			Test / Sandbox (push) Successful in 1m53s
				
			Test / Hakurei (push) Successful in 3m18s
				
			Test / Sandbox (race detector) (push) Successful in 3m40s
				
			Test / Hpkg (push) Successful in 3m35s
				
			Test / Hakurei (race detector) (push) Successful in 5m19s
				
			Test / Flake checks (push) Successful in 1m39s
				
			These are very useful in many packages containing relatively large amount of code making calls to difficult or impossible to stub functions. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
		
							parent
							
								
									b489a3bba1
								
							
						
					
					
						commit
						49600a6f46
					
				| @ -4,6 +4,8 @@ import ( | |||||||
| 	"errors" | 	"errors" | ||||||
| 	"os" | 	"os" | ||||||
| 	"testing" | 	"testing" | ||||||
|  | 
 | ||||||
|  | 	"hakurei.app/container/stub" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func TestAutoEtcOp(t *testing.T) { | func TestAutoEtcOp(t *testing.T) { | ||||||
| @ -17,22 +19,22 @@ func TestAutoEtcOp(t *testing.T) { | |||||||
| 	checkOpBehaviour(t, []opBehaviourTestCase{ | 	checkOpBehaviour(t, []opBehaviourTestCase{ | ||||||
| 		{"mkdirAll", new(Params), &AutoEtcOp{ | 		{"mkdirAll", new(Params), &AutoEtcOp{ | ||||||
| 			Prefix: "81ceabb30d37bbdb3868004629cb84e9", | 			Prefix: "81ceabb30d37bbdb3868004629cb84e9", | ||||||
| 		}, nil, nil, []kexpect{ | 		}, nil, nil, []stub.Call{ | ||||||
| 			{"mkdirAll", expectArgs{"/sysroot/etc/", os.FileMode(0755)}, nil, errUnique}, | 			{"mkdirAll", stub.ExpectArgs{"/sysroot/etc/", os.FileMode(0755)}, nil, stub.UniqueError(3)}, | ||||||
| 		}, errUnique}, | 		}, stub.UniqueError(3)}, | ||||||
| 
 | 
 | ||||||
| 		{"readdir", new(Params), &AutoEtcOp{ | 		{"readdir", new(Params), &AutoEtcOp{ | ||||||
| 			Prefix: "81ceabb30d37bbdb3868004629cb84e9", | 			Prefix: "81ceabb30d37bbdb3868004629cb84e9", | ||||||
| 		}, nil, nil, []kexpect{ | 		}, nil, nil, []stub.Call{ | ||||||
| 			{"mkdirAll", expectArgs{"/sysroot/etc/", os.FileMode(0755)}, nil, nil}, | 			{"mkdirAll", stub.ExpectArgs{"/sysroot/etc/", os.FileMode(0755)}, nil, nil}, | ||||||
| 			{"readdir", expectArgs{"/sysroot/etc/.host/81ceabb30d37bbdb3868004629cb84e9"}, stubDir(), errUnique}, | 			{"readdir", stub.ExpectArgs{"/sysroot/etc/.host/81ceabb30d37bbdb3868004629cb84e9"}, stubDir(), stub.UniqueError(2)}, | ||||||
| 		}, errUnique}, | 		}, stub.UniqueError(2)}, | ||||||
| 
 | 
 | ||||||
| 		{"symlink", new(Params), &AutoEtcOp{ | 		{"symlink", new(Params), &AutoEtcOp{ | ||||||
| 			Prefix: "81ceabb30d37bbdb3868004629cb84e9", | 			Prefix: "81ceabb30d37bbdb3868004629cb84e9", | ||||||
| 		}, nil, nil, []kexpect{ | 		}, nil, nil, []stub.Call{ | ||||||
| 			{"mkdirAll", expectArgs{"/sysroot/etc/", os.FileMode(0755)}, nil, nil}, | 			{"mkdirAll", stub.ExpectArgs{"/sysroot/etc/", os.FileMode(0755)}, nil, nil}, | ||||||
| 			{"readdir", expectArgs{"/sysroot/etc/.host/81ceabb30d37bbdb3868004629cb84e9"}, stubDir(".host", | 			{"readdir", stub.ExpectArgs{"/sysroot/etc/.host/81ceabb30d37bbdb3868004629cb84e9"}, stubDir(".host", | ||||||
| 				"alsa", "bash_logout", "bashrc", "binfmt.d", "dbus-1", "default", "dhcpcd.exit-hook", "fonts", | 				"alsa", "bash_logout", "bashrc", "binfmt.d", "dbus-1", "default", "dhcpcd.exit-hook", "fonts", | ||||||
| 				"fstab", "fuse.conf", "group", "host.conf", "hostname", "hosts", "hsurc", "inputrc", "issue", "kbd", | 				"fstab", "fuse.conf", "group", "host.conf", "hostname", "hosts", "hsurc", "inputrc", "issue", "kbd", | ||||||
| 				"locale.conf", "login.defs", "lsb-release", "lvm", "machine-id", "man_db.conf", "mdadm.conf", | 				"locale.conf", "login.defs", "lsb-release", "lvm", "machine-id", "man_db.conf", "mdadm.conf", | ||||||
| @ -41,14 +43,14 @@ func TestAutoEtcOp(t *testing.T) { | |||||||
| 				"protocols", "resolv.conf", "resolvconf.conf", "rpc", "services", "set-environment", "shadow", "shells", | 				"protocols", "resolv.conf", "resolvconf.conf", "rpc", "services", "set-environment", "shadow", "shells", | ||||||
| 				"ssh", "ssl", "static", "subgid", "subuid", "sudoers", "sway", "sysctl.d", "systemd", "terminfo", | 				"ssh", "ssl", "static", "subgid", "subuid", "sudoers", "sway", "sysctl.d", "systemd", "terminfo", | ||||||
| 				"tmpfiles.d", "udev", "vconsole.conf", "X11", "xdg", "zoneinfo"), nil}, | 				"tmpfiles.d", "udev", "vconsole.conf", "X11", "xdg", "zoneinfo"), nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/alsa", "/sysroot/etc/alsa"}, nil, errUnique}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/alsa", "/sysroot/etc/alsa"}, nil, stub.UniqueError(1)}, | ||||||
| 		}, errUnique}, | 		}, stub.UniqueError(1)}, | ||||||
| 
 | 
 | ||||||
| 		{"symlink mtab", new(Params), &AutoEtcOp{ | 		{"symlink mtab", new(Params), &AutoEtcOp{ | ||||||
| 			Prefix: "81ceabb30d37bbdb3868004629cb84e9", | 			Prefix: "81ceabb30d37bbdb3868004629cb84e9", | ||||||
| 		}, nil, nil, []kexpect{ | 		}, nil, nil, []stub.Call{ | ||||||
| 			{"mkdirAll", expectArgs{"/sysroot/etc/", os.FileMode(0755)}, nil, nil}, | 			{"mkdirAll", stub.ExpectArgs{"/sysroot/etc/", os.FileMode(0755)}, nil, nil}, | ||||||
| 			{"readdir", expectArgs{"/sysroot/etc/.host/81ceabb30d37bbdb3868004629cb84e9"}, stubDir(".host", | 			{"readdir", stub.ExpectArgs{"/sysroot/etc/.host/81ceabb30d37bbdb3868004629cb84e9"}, stubDir(".host", | ||||||
| 				"alsa", "bash_logout", "bashrc", "binfmt.d", "dbus-1", "default", "dhcpcd.exit-hook", "fonts", | 				"alsa", "bash_logout", "bashrc", "binfmt.d", "dbus-1", "default", "dhcpcd.exit-hook", "fonts", | ||||||
| 				"fstab", "fuse.conf", "group", "host.conf", "hostname", "hosts", "hsurc", "inputrc", "issue", "kbd", | 				"fstab", "fuse.conf", "group", "host.conf", "hostname", "hosts", "hsurc", "inputrc", "issue", "kbd", | ||||||
| 				"locale.conf", "login.defs", "lsb-release", "lvm", "machine-id", "man_db.conf", "mdadm.conf", | 				"locale.conf", "login.defs", "lsb-release", "lvm", "machine-id", "man_db.conf", "mdadm.conf", | ||||||
| @ -57,40 +59,40 @@ func TestAutoEtcOp(t *testing.T) { | |||||||
| 				"protocols", "resolv.conf", "resolvconf.conf", "rpc", "services", "set-environment", "shadow", "shells", | 				"protocols", "resolv.conf", "resolvconf.conf", "rpc", "services", "set-environment", "shadow", "shells", | ||||||
| 				"ssh", "ssl", "static", "subgid", "subuid", "sudoers", "sway", "sysctl.d", "systemd", "terminfo", | 				"ssh", "ssl", "static", "subgid", "subuid", "sudoers", "sway", "sysctl.d", "systemd", "terminfo", | ||||||
| 				"tmpfiles.d", "udev", "vconsole.conf", "X11", "xdg", "zoneinfo"), nil}, | 				"tmpfiles.d", "udev", "vconsole.conf", "X11", "xdg", "zoneinfo"), nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/alsa", "/sysroot/etc/alsa"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/alsa", "/sysroot/etc/alsa"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/bash_logout", "/sysroot/etc/bash_logout"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/bash_logout", "/sysroot/etc/bash_logout"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/bashrc", "/sysroot/etc/bashrc"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/bashrc", "/sysroot/etc/bashrc"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/binfmt.d", "/sysroot/etc/binfmt.d"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/binfmt.d", "/sysroot/etc/binfmt.d"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/dbus-1", "/sysroot/etc/dbus-1"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/dbus-1", "/sysroot/etc/dbus-1"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/default", "/sysroot/etc/default"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/default", "/sysroot/etc/default"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/dhcpcd.exit-hook", "/sysroot/etc/dhcpcd.exit-hook"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/dhcpcd.exit-hook", "/sysroot/etc/dhcpcd.exit-hook"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/fonts", "/sysroot/etc/fonts"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/fonts", "/sysroot/etc/fonts"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/fstab", "/sysroot/etc/fstab"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/fstab", "/sysroot/etc/fstab"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/fuse.conf", "/sysroot/etc/fuse.conf"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/fuse.conf", "/sysroot/etc/fuse.conf"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/host.conf", "/sysroot/etc/host.conf"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/host.conf", "/sysroot/etc/host.conf"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/hostname", "/sysroot/etc/hostname"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/hostname", "/sysroot/etc/hostname"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/hosts", "/sysroot/etc/hosts"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/hosts", "/sysroot/etc/hosts"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/hsurc", "/sysroot/etc/hsurc"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/hsurc", "/sysroot/etc/hsurc"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/inputrc", "/sysroot/etc/inputrc"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/inputrc", "/sysroot/etc/inputrc"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/issue", "/sysroot/etc/issue"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/issue", "/sysroot/etc/issue"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/kbd", "/sysroot/etc/kbd"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/kbd", "/sysroot/etc/kbd"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/locale.conf", "/sysroot/etc/locale.conf"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/locale.conf", "/sysroot/etc/locale.conf"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/login.defs", "/sysroot/etc/login.defs"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/login.defs", "/sysroot/etc/login.defs"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/lsb-release", "/sysroot/etc/lsb-release"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/lsb-release", "/sysroot/etc/lsb-release"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/lvm", "/sysroot/etc/lvm"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/lvm", "/sysroot/etc/lvm"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/machine-id", "/sysroot/etc/machine-id"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/machine-id", "/sysroot/etc/machine-id"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/man_db.conf", "/sysroot/etc/man_db.conf"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/man_db.conf", "/sysroot/etc/man_db.conf"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/mdadm.conf", "/sysroot/etc/mdadm.conf"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/mdadm.conf", "/sysroot/etc/mdadm.conf"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/modprobe.d", "/sysroot/etc/modprobe.d"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/modprobe.d", "/sysroot/etc/modprobe.d"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/modules-load.d", "/sysroot/etc/modules-load.d"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/modules-load.d", "/sysroot/etc/modules-load.d"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{"/proc/mounts", "/sysroot/etc/mtab"}, nil, errUnique}, | 			{"symlink", stub.ExpectArgs{"/proc/mounts", "/sysroot/etc/mtab"}, nil, stub.UniqueError(0)}, | ||||||
| 		}, errUnique}, | 		}, stub.UniqueError(0)}, | ||||||
| 
 | 
 | ||||||
| 		{"success nested", new(Params), &AutoEtcOp{ | 		{"success nested", new(Params), &AutoEtcOp{ | ||||||
| 			Prefix: "81ceabb30d37bbdb3868004629cb84e9", | 			Prefix: "81ceabb30d37bbdb3868004629cb84e9", | ||||||
| 		}, nil, nil, []kexpect{ | 		}, nil, nil, []stub.Call{ | ||||||
| 			{"mkdirAll", expectArgs{"/sysroot/etc/", os.FileMode(0755)}, nil, nil}, | 			{"mkdirAll", stub.ExpectArgs{"/sysroot/etc/", os.FileMode(0755)}, nil, nil}, | ||||||
| 			{"readdir", expectArgs{"/sysroot/etc/.host/81ceabb30d37bbdb3868004629cb84e9"}, stubDir(".host", | 			{"readdir", stub.ExpectArgs{"/sysroot/etc/.host/81ceabb30d37bbdb3868004629cb84e9"}, stubDir(".host", | ||||||
| 				"alsa", "bash_logout", "bashrc", "binfmt.d", "dbus-1", "default", "dhcpcd.exit-hook", "fonts", | 				"alsa", "bash_logout", "bashrc", "binfmt.d", "dbus-1", "default", "dhcpcd.exit-hook", "fonts", | ||||||
| 				"fstab", "fuse.conf", "group", "host.conf", "hostname", "hosts", "hsurc", "inputrc", "issue", "kbd", | 				"fstab", "fuse.conf", "group", "host.conf", "hostname", "hosts", "hsurc", "inputrc", "issue", "kbd", | ||||||
| 				"locale.conf", "login.defs", "lsb-release", "lvm", "machine-id", "man_db.conf", "mdadm.conf", | 				"locale.conf", "login.defs", "lsb-release", "lvm", "machine-id", "man_db.conf", "mdadm.conf", | ||||||
| @ -99,78 +101,78 @@ func TestAutoEtcOp(t *testing.T) { | |||||||
| 				"protocols", "resolv.conf", "resolvconf.conf", "rpc", "services", "set-environment", "shadow", "shells", | 				"protocols", "resolv.conf", "resolvconf.conf", "rpc", "services", "set-environment", "shadow", "shells", | ||||||
| 				"ssh", "ssl", "static", "subgid", "subuid", "sudoers", "sway", "sysctl.d", "systemd", "terminfo", | 				"ssh", "ssl", "static", "subgid", "subuid", "sudoers", "sway", "sysctl.d", "systemd", "terminfo", | ||||||
| 				"tmpfiles.d", "udev", "vconsole.conf", "X11", "xdg", "zoneinfo"), nil}, | 				"tmpfiles.d", "udev", "vconsole.conf", "X11", "xdg", "zoneinfo"), nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/alsa", "/sysroot/etc/alsa"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/alsa", "/sysroot/etc/alsa"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/bash_logout", "/sysroot/etc/bash_logout"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/bash_logout", "/sysroot/etc/bash_logout"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/bashrc", "/sysroot/etc/bashrc"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/bashrc", "/sysroot/etc/bashrc"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/binfmt.d", "/sysroot/etc/binfmt.d"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/binfmt.d", "/sysroot/etc/binfmt.d"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/dbus-1", "/sysroot/etc/dbus-1"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/dbus-1", "/sysroot/etc/dbus-1"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/default", "/sysroot/etc/default"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/default", "/sysroot/etc/default"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/dhcpcd.exit-hook", "/sysroot/etc/dhcpcd.exit-hook"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/dhcpcd.exit-hook", "/sysroot/etc/dhcpcd.exit-hook"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/fonts", "/sysroot/etc/fonts"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/fonts", "/sysroot/etc/fonts"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/fstab", "/sysroot/etc/fstab"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/fstab", "/sysroot/etc/fstab"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/fuse.conf", "/sysroot/etc/fuse.conf"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/fuse.conf", "/sysroot/etc/fuse.conf"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/host.conf", "/sysroot/etc/host.conf"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/host.conf", "/sysroot/etc/host.conf"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/hostname", "/sysroot/etc/hostname"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/hostname", "/sysroot/etc/hostname"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/hosts", "/sysroot/etc/hosts"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/hosts", "/sysroot/etc/hosts"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/hsurc", "/sysroot/etc/hsurc"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/hsurc", "/sysroot/etc/hsurc"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/inputrc", "/sysroot/etc/inputrc"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/inputrc", "/sysroot/etc/inputrc"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/issue", "/sysroot/etc/issue"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/issue", "/sysroot/etc/issue"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/kbd", "/sysroot/etc/kbd"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/kbd", "/sysroot/etc/kbd"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/locale.conf", "/sysroot/etc/locale.conf"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/locale.conf", "/sysroot/etc/locale.conf"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/login.defs", "/sysroot/etc/login.defs"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/login.defs", "/sysroot/etc/login.defs"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/lsb-release", "/sysroot/etc/lsb-release"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/lsb-release", "/sysroot/etc/lsb-release"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/lvm", "/sysroot/etc/lvm"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/lvm", "/sysroot/etc/lvm"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/machine-id", "/sysroot/etc/machine-id"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/machine-id", "/sysroot/etc/machine-id"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/man_db.conf", "/sysroot/etc/man_db.conf"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/man_db.conf", "/sysroot/etc/man_db.conf"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/mdadm.conf", "/sysroot/etc/mdadm.conf"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/mdadm.conf", "/sysroot/etc/mdadm.conf"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/modprobe.d", "/sysroot/etc/modprobe.d"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/modprobe.d", "/sysroot/etc/modprobe.d"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/modules-load.d", "/sysroot/etc/modules-load.d"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/modules-load.d", "/sysroot/etc/modules-load.d"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{"/proc/mounts", "/sysroot/etc/mtab"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{"/proc/mounts", "/sysroot/etc/mtab"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/nanorc", "/sysroot/etc/nanorc"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/nanorc", "/sysroot/etc/nanorc"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/netgroup", "/sysroot/etc/netgroup"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/netgroup", "/sysroot/etc/netgroup"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/nix", "/sysroot/etc/nix"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/nix", "/sysroot/etc/nix"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/nixos", "/sysroot/etc/nixos"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/nixos", "/sysroot/etc/nixos"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/NIXOS", "/sysroot/etc/NIXOS"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/NIXOS", "/sysroot/etc/NIXOS"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/nscd.conf", "/sysroot/etc/nscd.conf"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/nscd.conf", "/sysroot/etc/nscd.conf"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/nsswitch.conf", "/sysroot/etc/nsswitch.conf"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/nsswitch.conf", "/sysroot/etc/nsswitch.conf"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/os-release", "/sysroot/etc/os-release"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/os-release", "/sysroot/etc/os-release"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/pam", "/sysroot/etc/pam"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/pam", "/sysroot/etc/pam"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/pam.d", "/sysroot/etc/pam.d"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/pam.d", "/sysroot/etc/pam.d"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/pipewire", "/sysroot/etc/pipewire"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/pipewire", "/sysroot/etc/pipewire"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/pki", "/sysroot/etc/pki"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/pki", "/sysroot/etc/pki"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/polkit-1", "/sysroot/etc/polkit-1"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/polkit-1", "/sysroot/etc/polkit-1"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/profile", "/sysroot/etc/profile"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/profile", "/sysroot/etc/profile"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/protocols", "/sysroot/etc/protocols"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/protocols", "/sysroot/etc/protocols"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/resolv.conf", "/sysroot/etc/resolv.conf"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/resolv.conf", "/sysroot/etc/resolv.conf"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/resolvconf.conf", "/sysroot/etc/resolvconf.conf"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/resolvconf.conf", "/sysroot/etc/resolvconf.conf"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/rpc", "/sysroot/etc/rpc"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/rpc", "/sysroot/etc/rpc"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/services", "/sysroot/etc/services"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/services", "/sysroot/etc/services"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/set-environment", "/sysroot/etc/set-environment"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/set-environment", "/sysroot/etc/set-environment"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/shadow", "/sysroot/etc/shadow"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/shadow", "/sysroot/etc/shadow"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/shells", "/sysroot/etc/shells"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/shells", "/sysroot/etc/shells"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/ssh", "/sysroot/etc/ssh"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/ssh", "/sysroot/etc/ssh"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/ssl", "/sysroot/etc/ssl"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/ssl", "/sysroot/etc/ssl"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/static", "/sysroot/etc/static"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/static", "/sysroot/etc/static"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/subgid", "/sysroot/etc/subgid"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/subgid", "/sysroot/etc/subgid"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/subuid", "/sysroot/etc/subuid"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/subuid", "/sysroot/etc/subuid"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/sudoers", "/sysroot/etc/sudoers"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/sudoers", "/sysroot/etc/sudoers"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/sway", "/sysroot/etc/sway"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/sway", "/sysroot/etc/sway"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/sysctl.d", "/sysroot/etc/sysctl.d"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/sysctl.d", "/sysroot/etc/sysctl.d"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/systemd", "/sysroot/etc/systemd"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/systemd", "/sysroot/etc/systemd"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/terminfo", "/sysroot/etc/terminfo"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/terminfo", "/sysroot/etc/terminfo"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/tmpfiles.d", "/sysroot/etc/tmpfiles.d"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/tmpfiles.d", "/sysroot/etc/tmpfiles.d"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/udev", "/sysroot/etc/udev"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/udev", "/sysroot/etc/udev"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/vconsole.conf", "/sysroot/etc/vconsole.conf"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/vconsole.conf", "/sysroot/etc/vconsole.conf"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/X11", "/sysroot/etc/X11"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/X11", "/sysroot/etc/X11"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/xdg", "/sysroot/etc/xdg"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/xdg", "/sysroot/etc/xdg"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/zoneinfo", "/sysroot/etc/zoneinfo"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/zoneinfo", "/sysroot/etc/zoneinfo"}, nil, nil}, | ||||||
| 		}, nil}, | 		}, nil}, | ||||||
| 
 | 
 | ||||||
| 		{"success", new(Params), &AutoEtcOp{ | 		{"success", new(Params), &AutoEtcOp{ | ||||||
| 			Prefix: "81ceabb30d37bbdb3868004629cb84e9", | 			Prefix: "81ceabb30d37bbdb3868004629cb84e9", | ||||||
| 		}, nil, nil, []kexpect{ | 		}, nil, nil, []stub.Call{ | ||||||
| 			{"mkdirAll", expectArgs{"/sysroot/etc/", os.FileMode(0755)}, nil, nil}, | 			{"mkdirAll", stub.ExpectArgs{"/sysroot/etc/", os.FileMode(0755)}, nil, nil}, | ||||||
| 			{"readdir", expectArgs{"/sysroot/etc/.host/81ceabb30d37bbdb3868004629cb84e9"}, stubDir( | 			{"readdir", stub.ExpectArgs{"/sysroot/etc/.host/81ceabb30d37bbdb3868004629cb84e9"}, stubDir( | ||||||
| 				"alsa", "bash_logout", "bashrc", "binfmt.d", "dbus-1", "default", "dhcpcd.exit-hook", "fonts", | 				"alsa", "bash_logout", "bashrc", "binfmt.d", "dbus-1", "default", "dhcpcd.exit-hook", "fonts", | ||||||
| 				"fstab", "fuse.conf", "group", "host.conf", "hostname", "hosts", "hsurc", "inputrc", "issue", "kbd", | 				"fstab", "fuse.conf", "group", "host.conf", "hostname", "hosts", "hsurc", "inputrc", "issue", "kbd", | ||||||
| 				"locale.conf", "login.defs", "lsb-release", "lvm", "machine-id", "man_db.conf", "mdadm.conf", | 				"locale.conf", "login.defs", "lsb-release", "lvm", "machine-id", "man_db.conf", "mdadm.conf", | ||||||
| @ -179,71 +181,71 @@ func TestAutoEtcOp(t *testing.T) { | |||||||
| 				"protocols", "resolv.conf", "resolvconf.conf", "rpc", "services", "set-environment", "shadow", "shells", | 				"protocols", "resolv.conf", "resolvconf.conf", "rpc", "services", "set-environment", "shadow", "shells", | ||||||
| 				"ssh", "ssl", "static", "subgid", "subuid", "sudoers", "sway", "sysctl.d", "systemd", "terminfo", | 				"ssh", "ssl", "static", "subgid", "subuid", "sudoers", "sway", "sysctl.d", "systemd", "terminfo", | ||||||
| 				"tmpfiles.d", "udev", "vconsole.conf", "X11", "xdg", "zoneinfo"), nil}, | 				"tmpfiles.d", "udev", "vconsole.conf", "X11", "xdg", "zoneinfo"), nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/alsa", "/sysroot/etc/alsa"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/alsa", "/sysroot/etc/alsa"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/bash_logout", "/sysroot/etc/bash_logout"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/bash_logout", "/sysroot/etc/bash_logout"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/bashrc", "/sysroot/etc/bashrc"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/bashrc", "/sysroot/etc/bashrc"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/binfmt.d", "/sysroot/etc/binfmt.d"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/binfmt.d", "/sysroot/etc/binfmt.d"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/dbus-1", "/sysroot/etc/dbus-1"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/dbus-1", "/sysroot/etc/dbus-1"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/default", "/sysroot/etc/default"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/default", "/sysroot/etc/default"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/dhcpcd.exit-hook", "/sysroot/etc/dhcpcd.exit-hook"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/dhcpcd.exit-hook", "/sysroot/etc/dhcpcd.exit-hook"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/fonts", "/sysroot/etc/fonts"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/fonts", "/sysroot/etc/fonts"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/fstab", "/sysroot/etc/fstab"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/fstab", "/sysroot/etc/fstab"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/fuse.conf", "/sysroot/etc/fuse.conf"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/fuse.conf", "/sysroot/etc/fuse.conf"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/host.conf", "/sysroot/etc/host.conf"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/host.conf", "/sysroot/etc/host.conf"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/hostname", "/sysroot/etc/hostname"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/hostname", "/sysroot/etc/hostname"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/hosts", "/sysroot/etc/hosts"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/hosts", "/sysroot/etc/hosts"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/hsurc", "/sysroot/etc/hsurc"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/hsurc", "/sysroot/etc/hsurc"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/inputrc", "/sysroot/etc/inputrc"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/inputrc", "/sysroot/etc/inputrc"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/issue", "/sysroot/etc/issue"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/issue", "/sysroot/etc/issue"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/kbd", "/sysroot/etc/kbd"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/kbd", "/sysroot/etc/kbd"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/locale.conf", "/sysroot/etc/locale.conf"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/locale.conf", "/sysroot/etc/locale.conf"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/login.defs", "/sysroot/etc/login.defs"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/login.defs", "/sysroot/etc/login.defs"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/lsb-release", "/sysroot/etc/lsb-release"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/lsb-release", "/sysroot/etc/lsb-release"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/lvm", "/sysroot/etc/lvm"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/lvm", "/sysroot/etc/lvm"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/machine-id", "/sysroot/etc/machine-id"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/machine-id", "/sysroot/etc/machine-id"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/man_db.conf", "/sysroot/etc/man_db.conf"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/man_db.conf", "/sysroot/etc/man_db.conf"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/mdadm.conf", "/sysroot/etc/mdadm.conf"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/mdadm.conf", "/sysroot/etc/mdadm.conf"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/modprobe.d", "/sysroot/etc/modprobe.d"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/modprobe.d", "/sysroot/etc/modprobe.d"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/modules-load.d", "/sysroot/etc/modules-load.d"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/modules-load.d", "/sysroot/etc/modules-load.d"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{"/proc/mounts", "/sysroot/etc/mtab"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{"/proc/mounts", "/sysroot/etc/mtab"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/nanorc", "/sysroot/etc/nanorc"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/nanorc", "/sysroot/etc/nanorc"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/netgroup", "/sysroot/etc/netgroup"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/netgroup", "/sysroot/etc/netgroup"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/nix", "/sysroot/etc/nix"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/nix", "/sysroot/etc/nix"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/nixos", "/sysroot/etc/nixos"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/nixos", "/sysroot/etc/nixos"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/NIXOS", "/sysroot/etc/NIXOS"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/NIXOS", "/sysroot/etc/NIXOS"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/nscd.conf", "/sysroot/etc/nscd.conf"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/nscd.conf", "/sysroot/etc/nscd.conf"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/nsswitch.conf", "/sysroot/etc/nsswitch.conf"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/nsswitch.conf", "/sysroot/etc/nsswitch.conf"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/os-release", "/sysroot/etc/os-release"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/os-release", "/sysroot/etc/os-release"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/pam", "/sysroot/etc/pam"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/pam", "/sysroot/etc/pam"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/pam.d", "/sysroot/etc/pam.d"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/pam.d", "/sysroot/etc/pam.d"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/pipewire", "/sysroot/etc/pipewire"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/pipewire", "/sysroot/etc/pipewire"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/pki", "/sysroot/etc/pki"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/pki", "/sysroot/etc/pki"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/polkit-1", "/sysroot/etc/polkit-1"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/polkit-1", "/sysroot/etc/polkit-1"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/profile", "/sysroot/etc/profile"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/profile", "/sysroot/etc/profile"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/protocols", "/sysroot/etc/protocols"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/protocols", "/sysroot/etc/protocols"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/resolv.conf", "/sysroot/etc/resolv.conf"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/resolv.conf", "/sysroot/etc/resolv.conf"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/resolvconf.conf", "/sysroot/etc/resolvconf.conf"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/resolvconf.conf", "/sysroot/etc/resolvconf.conf"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/rpc", "/sysroot/etc/rpc"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/rpc", "/sysroot/etc/rpc"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/services", "/sysroot/etc/services"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/services", "/sysroot/etc/services"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/set-environment", "/sysroot/etc/set-environment"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/set-environment", "/sysroot/etc/set-environment"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/shadow", "/sysroot/etc/shadow"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/shadow", "/sysroot/etc/shadow"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/shells", "/sysroot/etc/shells"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/shells", "/sysroot/etc/shells"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/ssh", "/sysroot/etc/ssh"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/ssh", "/sysroot/etc/ssh"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/ssl", "/sysroot/etc/ssl"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/ssl", "/sysroot/etc/ssl"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/static", "/sysroot/etc/static"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/static", "/sysroot/etc/static"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/subgid", "/sysroot/etc/subgid"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/subgid", "/sysroot/etc/subgid"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/subuid", "/sysroot/etc/subuid"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/subuid", "/sysroot/etc/subuid"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/sudoers", "/sysroot/etc/sudoers"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/sudoers", "/sysroot/etc/sudoers"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/sway", "/sysroot/etc/sway"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/sway", "/sysroot/etc/sway"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/sysctl.d", "/sysroot/etc/sysctl.d"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/sysctl.d", "/sysroot/etc/sysctl.d"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/systemd", "/sysroot/etc/systemd"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/systemd", "/sysroot/etc/systemd"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/terminfo", "/sysroot/etc/terminfo"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/terminfo", "/sysroot/etc/terminfo"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/tmpfiles.d", "/sysroot/etc/tmpfiles.d"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/tmpfiles.d", "/sysroot/etc/tmpfiles.d"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/udev", "/sysroot/etc/udev"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/udev", "/sysroot/etc/udev"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/vconsole.conf", "/sysroot/etc/vconsole.conf"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/vconsole.conf", "/sysroot/etc/vconsole.conf"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/X11", "/sysroot/etc/X11"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/X11", "/sysroot/etc/X11"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/xdg", "/sysroot/etc/xdg"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/xdg", "/sysroot/etc/xdg"}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/zoneinfo", "/sysroot/etc/zoneinfo"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{".host/81ceabb30d37bbdb3868004629cb84e9/zoneinfo", "/sysroot/etc/zoneinfo"}, nil, nil}, | ||||||
| 		}, nil}, | 		}, nil}, | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -4,6 +4,8 @@ import ( | |||||||
| 	"errors" | 	"errors" | ||||||
| 	"os" | 	"os" | ||||||
| 	"testing" | 	"testing" | ||||||
|  | 
 | ||||||
|  | 	"hakurei.app/container/stub" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func TestAutoRootOp(t *testing.T) { | func TestAutoRootOp(t *testing.T) { | ||||||
| @ -18,100 +20,100 @@ func TestAutoRootOp(t *testing.T) { | |||||||
| 		{"readdir", &Params{ParentPerm: 0750}, &AutoRootOp{ | 		{"readdir", &Params{ParentPerm: 0750}, &AutoRootOp{ | ||||||
| 			Host:  MustAbs("/"), | 			Host:  MustAbs("/"), | ||||||
| 			Flags: BindWritable, | 			Flags: BindWritable, | ||||||
| 		}, []kexpect{ | 		}, []stub.Call{ | ||||||
| 			{"readdir", expectArgs{"/"}, stubDir(), errUnique}, | 			{"readdir", stub.ExpectArgs{"/"}, stubDir(), stub.UniqueError(2)}, | ||||||
| 		}, errUnique, nil, nil}, | 		}, stub.UniqueError(2), nil, nil}, | ||||||
| 
 | 
 | ||||||
| 		{"early", &Params{ParentPerm: 0750}, &AutoRootOp{ | 		{"early", &Params{ParentPerm: 0750}, &AutoRootOp{ | ||||||
| 			Host:  MustAbs("/"), | 			Host:  MustAbs("/"), | ||||||
| 			Flags: BindWritable, | 			Flags: BindWritable, | ||||||
| 		}, []kexpect{ | 		}, []stub.Call{ | ||||||
| 			{"readdir", expectArgs{"/"}, stubDir("bin", "dev", "etc", "home", "lib64", | 			{"readdir", stub.ExpectArgs{"/"}, stubDir("bin", "dev", "etc", "home", "lib64", | ||||||
| 				"lost+found", "mnt", "nix", "proc", "root", "run", "srv", "sys", "tmp", "usr", "var"), nil}, | 				"lost+found", "mnt", "nix", "proc", "root", "run", "srv", "sys", "tmp", "usr", "var"), nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/bin"}, "", errUnique}, | 			{"evalSymlinks", stub.ExpectArgs{"/bin"}, "", stub.UniqueError(1)}, | ||||||
| 		}, errUnique, nil, nil}, | 		}, stub.UniqueError(1), nil, nil}, | ||||||
| 
 | 
 | ||||||
| 		{"apply", &Params{ParentPerm: 0750}, &AutoRootOp{ | 		{"apply", &Params{ParentPerm: 0750}, &AutoRootOp{ | ||||||
| 			Host:  MustAbs("/"), | 			Host:  MustAbs("/"), | ||||||
| 			Flags: BindWritable, | 			Flags: BindWritable, | ||||||
| 		}, []kexpect{ | 		}, []stub.Call{ | ||||||
| 			{"readdir", expectArgs{"/"}, stubDir("bin", "dev", "etc", "home", "lib64", | 			{"readdir", stub.ExpectArgs{"/"}, stubDir("bin", "dev", "etc", "home", "lib64", | ||||||
| 				"lost+found", "mnt", "nix", "proc", "root", "run", "srv", "sys", "tmp", "usr", "var"), nil}, | 				"lost+found", "mnt", "nix", "proc", "root", "run", "srv", "sys", "tmp", "usr", "var"), nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/bin"}, "/usr/bin", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/bin"}, "/usr/bin", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/home"}, "/home", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/home"}, "/home", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/lib64"}, "/lib64", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/lib64"}, "/lib64", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/lost+found"}, "/lost+found", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/lost+found"}, "/lost+found", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/nix"}, "/nix", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/nix"}, "/nix", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/root"}, "/root", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/root"}, "/root", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/run"}, "/run", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/run"}, "/run", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/srv"}, "/srv", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/srv"}, "/srv", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/sys"}, "/sys", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/sys"}, "/sys", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/usr"}, "/usr", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/usr"}, "/usr", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/var"}, "/var", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/var"}, "/var", nil}, | ||||||
| 		}, nil, []kexpect{ | 		}, nil, []stub.Call{ | ||||||
| 			{"verbosef", expectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/usr/bin"), MustAbs("/bin"), MustAbs("/bin"), BindWritable}}}, nil, nil}, | 			{"verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/usr/bin"), MustAbs("/bin"), MustAbs("/bin"), BindWritable}}}, nil, nil}, | ||||||
| 			{"stat", expectArgs{"/host/usr/bin"}, isDirFi(false), errUnique}, | 			{"stat", stub.ExpectArgs{"/host/usr/bin"}, isDirFi(false), stub.UniqueError(0)}, | ||||||
| 		}, errUnique}, | 		}, stub.UniqueError(0)}, | ||||||
| 
 | 
 | ||||||
| 		{"success pd", &Params{ParentPerm: 0750}, &AutoRootOp{ | 		{"success pd", &Params{ParentPerm: 0750}, &AutoRootOp{ | ||||||
| 			Host:  MustAbs("/"), | 			Host:  MustAbs("/"), | ||||||
| 			Flags: BindWritable, | 			Flags: BindWritable, | ||||||
| 		}, []kexpect{ | 		}, []stub.Call{ | ||||||
| 			{"readdir", expectArgs{"/"}, stubDir("bin", "dev", "etc", "home", "lib64", | 			{"readdir", stub.ExpectArgs{"/"}, stubDir("bin", "dev", "etc", "home", "lib64", | ||||||
| 				"lost+found", "mnt", "nix", "proc", "root", "run", "srv", "sys", "tmp", "usr", "var"), nil}, | 				"lost+found", "mnt", "nix", "proc", "root", "run", "srv", "sys", "tmp", "usr", "var"), nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/bin"}, "/usr/bin", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/bin"}, "/usr/bin", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/home"}, "/home", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/home"}, "/home", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/lib64"}, "/lib64", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/lib64"}, "/lib64", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/lost+found"}, "/lost+found", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/lost+found"}, "/lost+found", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/nix"}, "/nix", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/nix"}, "/nix", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/root"}, "/root", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/root"}, "/root", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/run"}, "/run", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/run"}, "/run", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/srv"}, "/srv", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/srv"}, "/srv", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/sys"}, "/sys", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/sys"}, "/sys", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/usr"}, "/usr", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/usr"}, "/usr", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/var"}, "/var", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/var"}, "/var", nil}, | ||||||
| 		}, nil, []kexpect{ | 		}, nil, []stub.Call{ | ||||||
| 			{"verbosef", expectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/usr/bin"), MustAbs("/bin"), MustAbs("/bin"), BindWritable}}}, nil, nil}, {"stat", expectArgs{"/host/usr/bin"}, isDirFi(true), nil}, {"mkdirAll", expectArgs{"/sysroot/bin", os.FileMode(0700)}, nil, nil}, {"bindMount", expectArgs{"/host/usr/bin", "/sysroot/bin", uintptr(0x4004), false}, nil, nil}, | 			{"verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/usr/bin"), MustAbs("/bin"), MustAbs("/bin"), BindWritable}}}, nil, nil}, {"stat", stub.ExpectArgs{"/host/usr/bin"}, isDirFi(true), nil}, {"mkdirAll", stub.ExpectArgs{"/sysroot/bin", os.FileMode(0700)}, nil, nil}, {"bindMount", stub.ExpectArgs{"/host/usr/bin", "/sysroot/bin", uintptr(0x4004), false}, nil, nil}, | ||||||
| 			{"verbosef", expectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/home"), MustAbs("/home"), MustAbs("/home"), BindWritable}}}, nil, nil}, {"stat", expectArgs{"/host/home"}, isDirFi(true), nil}, {"mkdirAll", expectArgs{"/sysroot/home", os.FileMode(0700)}, nil, nil}, {"bindMount", expectArgs{"/host/home", "/sysroot/home", uintptr(0x4004), false}, nil, nil}, | 			{"verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/home"), MustAbs("/home"), MustAbs("/home"), BindWritable}}}, nil, nil}, {"stat", stub.ExpectArgs{"/host/home"}, isDirFi(true), nil}, {"mkdirAll", stub.ExpectArgs{"/sysroot/home", os.FileMode(0700)}, nil, nil}, {"bindMount", stub.ExpectArgs{"/host/home", "/sysroot/home", uintptr(0x4004), false}, nil, nil}, | ||||||
| 			{"verbosef", expectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/lib64"), MustAbs("/lib64"), MustAbs("/lib64"), BindWritable}}}, nil, nil}, {"stat", expectArgs{"/host/lib64"}, isDirFi(true), nil}, {"mkdirAll", expectArgs{"/sysroot/lib64", os.FileMode(0700)}, nil, nil}, {"bindMount", expectArgs{"/host/lib64", "/sysroot/lib64", uintptr(0x4004), false}, nil, nil}, | 			{"verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/lib64"), MustAbs("/lib64"), MustAbs("/lib64"), BindWritable}}}, nil, nil}, {"stat", stub.ExpectArgs{"/host/lib64"}, isDirFi(true), nil}, {"mkdirAll", stub.ExpectArgs{"/sysroot/lib64", os.FileMode(0700)}, nil, nil}, {"bindMount", stub.ExpectArgs{"/host/lib64", "/sysroot/lib64", uintptr(0x4004), false}, nil, nil}, | ||||||
| 			{"verbosef", expectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/lost+found"), MustAbs("/lost+found"), MustAbs("/lost+found"), BindWritable}}}, nil, nil}, {"stat", expectArgs{"/host/lost+found"}, isDirFi(true), nil}, {"mkdirAll", expectArgs{"/sysroot/lost+found", os.FileMode(0700)}, nil, nil}, {"bindMount", expectArgs{"/host/lost+found", "/sysroot/lost+found", uintptr(0x4004), false}, nil, nil}, | 			{"verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/lost+found"), MustAbs("/lost+found"), MustAbs("/lost+found"), BindWritable}}}, nil, nil}, {"stat", stub.ExpectArgs{"/host/lost+found"}, isDirFi(true), nil}, {"mkdirAll", stub.ExpectArgs{"/sysroot/lost+found", os.FileMode(0700)}, nil, nil}, {"bindMount", stub.ExpectArgs{"/host/lost+found", "/sysroot/lost+found", uintptr(0x4004), false}, nil, nil}, | ||||||
| 			{"verbosef", expectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/nix"), MustAbs("/nix"), MustAbs("/nix"), BindWritable}}}, nil, nil}, {"stat", expectArgs{"/host/nix"}, isDirFi(true), nil}, {"mkdirAll", expectArgs{"/sysroot/nix", os.FileMode(0700)}, nil, nil}, {"bindMount", expectArgs{"/host/nix", "/sysroot/nix", uintptr(0x4004), false}, nil, nil}, | 			{"verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/nix"), MustAbs("/nix"), MustAbs("/nix"), BindWritable}}}, nil, nil}, {"stat", stub.ExpectArgs{"/host/nix"}, isDirFi(true), nil}, {"mkdirAll", stub.ExpectArgs{"/sysroot/nix", os.FileMode(0700)}, nil, nil}, {"bindMount", stub.ExpectArgs{"/host/nix", "/sysroot/nix", uintptr(0x4004), false}, nil, nil}, | ||||||
| 			{"verbosef", expectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/root"), MustAbs("/root"), MustAbs("/root"), BindWritable}}}, nil, nil}, {"stat", expectArgs{"/host/root"}, isDirFi(true), nil}, {"mkdirAll", expectArgs{"/sysroot/root", os.FileMode(0700)}, nil, nil}, {"bindMount", expectArgs{"/host/root", "/sysroot/root", uintptr(0x4004), false}, nil, nil}, | 			{"verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/root"), MustAbs("/root"), MustAbs("/root"), BindWritable}}}, nil, nil}, {"stat", stub.ExpectArgs{"/host/root"}, isDirFi(true), nil}, {"mkdirAll", stub.ExpectArgs{"/sysroot/root", os.FileMode(0700)}, nil, nil}, {"bindMount", stub.ExpectArgs{"/host/root", "/sysroot/root", uintptr(0x4004), false}, nil, nil}, | ||||||
| 			{"verbosef", expectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/run"), MustAbs("/run"), MustAbs("/run"), BindWritable}}}, nil, nil}, {"stat", expectArgs{"/host/run"}, isDirFi(true), nil}, {"mkdirAll", expectArgs{"/sysroot/run", os.FileMode(0700)}, nil, nil}, {"bindMount", expectArgs{"/host/run", "/sysroot/run", uintptr(0x4004), false}, nil, nil}, | 			{"verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/run"), MustAbs("/run"), MustAbs("/run"), BindWritable}}}, nil, nil}, {"stat", stub.ExpectArgs{"/host/run"}, isDirFi(true), nil}, {"mkdirAll", stub.ExpectArgs{"/sysroot/run", os.FileMode(0700)}, nil, nil}, {"bindMount", stub.ExpectArgs{"/host/run", "/sysroot/run", uintptr(0x4004), false}, nil, nil}, | ||||||
| 			{"verbosef", expectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/srv"), MustAbs("/srv"), MustAbs("/srv"), BindWritable}}}, nil, nil}, {"stat", expectArgs{"/host/srv"}, isDirFi(true), nil}, {"mkdirAll", expectArgs{"/sysroot/srv", os.FileMode(0700)}, nil, nil}, {"bindMount", expectArgs{"/host/srv", "/sysroot/srv", uintptr(0x4004), false}, nil, nil}, | 			{"verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/srv"), MustAbs("/srv"), MustAbs("/srv"), BindWritable}}}, nil, nil}, {"stat", stub.ExpectArgs{"/host/srv"}, isDirFi(true), nil}, {"mkdirAll", stub.ExpectArgs{"/sysroot/srv", os.FileMode(0700)}, nil, nil}, {"bindMount", stub.ExpectArgs{"/host/srv", "/sysroot/srv", uintptr(0x4004), false}, nil, nil}, | ||||||
| 			{"verbosef", expectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/sys"), MustAbs("/sys"), MustAbs("/sys"), BindWritable}}}, nil, nil}, {"stat", expectArgs{"/host/sys"}, isDirFi(true), nil}, {"mkdirAll", expectArgs{"/sysroot/sys", os.FileMode(0700)}, nil, nil}, {"bindMount", expectArgs{"/host/sys", "/sysroot/sys", uintptr(0x4004), false}, nil, nil}, | 			{"verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/sys"), MustAbs("/sys"), MustAbs("/sys"), BindWritable}}}, nil, nil}, {"stat", stub.ExpectArgs{"/host/sys"}, isDirFi(true), nil}, {"mkdirAll", stub.ExpectArgs{"/sysroot/sys", os.FileMode(0700)}, nil, nil}, {"bindMount", stub.ExpectArgs{"/host/sys", "/sysroot/sys", uintptr(0x4004), false}, nil, nil}, | ||||||
| 			{"verbosef", expectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/usr"), MustAbs("/usr"), MustAbs("/usr"), BindWritable}}}, nil, nil}, {"stat", expectArgs{"/host/usr"}, isDirFi(true), nil}, {"mkdirAll", expectArgs{"/sysroot/usr", os.FileMode(0700)}, nil, nil}, {"bindMount", expectArgs{"/host/usr", "/sysroot/usr", uintptr(0x4004), false}, nil, nil}, | 			{"verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/usr"), MustAbs("/usr"), MustAbs("/usr"), BindWritable}}}, nil, nil}, {"stat", stub.ExpectArgs{"/host/usr"}, isDirFi(true), nil}, {"mkdirAll", stub.ExpectArgs{"/sysroot/usr", os.FileMode(0700)}, nil, nil}, {"bindMount", stub.ExpectArgs{"/host/usr", "/sysroot/usr", uintptr(0x4004), false}, nil, nil}, | ||||||
| 			{"verbosef", expectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/var"), MustAbs("/var"), MustAbs("/var"), BindWritable}}}, nil, nil}, {"stat", expectArgs{"/host/var"}, isDirFi(true), nil}, {"mkdirAll", expectArgs{"/sysroot/var", os.FileMode(0700)}, nil, nil}, {"bindMount", expectArgs{"/host/var", "/sysroot/var", uintptr(0x4004), false}, nil, nil}, | 			{"verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/var"), MustAbs("/var"), MustAbs("/var"), BindWritable}}}, nil, nil}, {"stat", stub.ExpectArgs{"/host/var"}, isDirFi(true), nil}, {"mkdirAll", stub.ExpectArgs{"/sysroot/var", os.FileMode(0700)}, nil, nil}, {"bindMount", stub.ExpectArgs{"/host/var", "/sysroot/var", uintptr(0x4004), false}, nil, nil}, | ||||||
| 		}, nil}, | 		}, nil}, | ||||||
| 
 | 
 | ||||||
| 		{"success", &Params{ParentPerm: 0750}, &AutoRootOp{ | 		{"success", &Params{ParentPerm: 0750}, &AutoRootOp{ | ||||||
| 			Host: MustAbs("/var/lib/planterette/base/debian:f92c9052"), | 			Host: MustAbs("/var/lib/planterette/base/debian:f92c9052"), | ||||||
| 		}, []kexpect{ | 		}, []stub.Call{ | ||||||
| 			{"readdir", expectArgs{"/var/lib/planterette/base/debian:f92c9052"}, stubDir("bin", "dev", "etc", "home", "lib64", | 			{"readdir", stub.ExpectArgs{"/var/lib/planterette/base/debian:f92c9052"}, stubDir("bin", "dev", "etc", "home", "lib64", | ||||||
| 				"lost+found", "mnt", "nix", "proc", "root", "run", "srv", "sys", "tmp", "usr", "var"), nil}, | 				"lost+found", "mnt", "nix", "proc", "root", "run", "srv", "sys", "tmp", "usr", "var"), nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/var/lib/planterette/base/debian:f92c9052/bin"}, "/var/lib/planterette/base/debian:f92c9052/usr/bin", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/var/lib/planterette/base/debian:f92c9052/bin"}, "/var/lib/planterette/base/debian:f92c9052/usr/bin", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/var/lib/planterette/base/debian:f92c9052/home"}, "/var/lib/planterette/base/debian:f92c9052/home", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/var/lib/planterette/base/debian:f92c9052/home"}, "/var/lib/planterette/base/debian:f92c9052/home", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/var/lib/planterette/base/debian:f92c9052/lib64"}, "/var/lib/planterette/base/debian:f92c9052/lib64", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/var/lib/planterette/base/debian:f92c9052/lib64"}, "/var/lib/planterette/base/debian:f92c9052/lib64", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/var/lib/planterette/base/debian:f92c9052/lost+found"}, "/var/lib/planterette/base/debian:f92c9052/lost+found", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/var/lib/planterette/base/debian:f92c9052/lost+found"}, "/var/lib/planterette/base/debian:f92c9052/lost+found", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/var/lib/planterette/base/debian:f92c9052/nix"}, "/var/lib/planterette/base/debian:f92c9052/nix", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/var/lib/planterette/base/debian:f92c9052/nix"}, "/var/lib/planterette/base/debian:f92c9052/nix", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/var/lib/planterette/base/debian:f92c9052/root"}, "/var/lib/planterette/base/debian:f92c9052/root", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/var/lib/planterette/base/debian:f92c9052/root"}, "/var/lib/planterette/base/debian:f92c9052/root", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/var/lib/planterette/base/debian:f92c9052/run"}, "/var/lib/planterette/base/debian:f92c9052/run", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/var/lib/planterette/base/debian:f92c9052/run"}, "/var/lib/planterette/base/debian:f92c9052/run", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/var/lib/planterette/base/debian:f92c9052/srv"}, "/var/lib/planterette/base/debian:f92c9052/srv", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/var/lib/planterette/base/debian:f92c9052/srv"}, "/var/lib/planterette/base/debian:f92c9052/srv", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/var/lib/planterette/base/debian:f92c9052/sys"}, "/var/lib/planterette/base/debian:f92c9052/sys", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/var/lib/planterette/base/debian:f92c9052/sys"}, "/var/lib/planterette/base/debian:f92c9052/sys", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/var/lib/planterette/base/debian:f92c9052/usr"}, "/var/lib/planterette/base/debian:f92c9052/usr", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/var/lib/planterette/base/debian:f92c9052/usr"}, "/var/lib/planterette/base/debian:f92c9052/usr", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/var/lib/planterette/base/debian:f92c9052/var"}, "/var/lib/planterette/base/debian:f92c9052/var", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/var/lib/planterette/base/debian:f92c9052/var"}, "/var/lib/planterette/base/debian:f92c9052/var", nil}, | ||||||
| 		}, nil, []kexpect{ | 		}, nil, []stub.Call{ | ||||||
| 			{"verbosef", expectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/var/lib/planterette/base/debian:f92c9052/usr/bin"), MustAbs("/var/lib/planterette/base/debian:f92c9052/bin"), MustAbs("/bin"), 0}}}, nil, nil}, {"stat", expectArgs{"/host/var/lib/planterette/base/debian:f92c9052/usr/bin"}, isDirFi(true), nil}, {"mkdirAll", expectArgs{"/sysroot/bin", os.FileMode(0700)}, nil, nil}, {"bindMount", expectArgs{"/host/var/lib/planterette/base/debian:f92c9052/usr/bin", "/sysroot/bin", uintptr(0x4005), false}, nil, nil}, | 			{"verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/var/lib/planterette/base/debian:f92c9052/usr/bin"), MustAbs("/var/lib/planterette/base/debian:f92c9052/bin"), MustAbs("/bin"), 0}}}, nil, nil}, {"stat", stub.ExpectArgs{"/host/var/lib/planterette/base/debian:f92c9052/usr/bin"}, isDirFi(true), nil}, {"mkdirAll", stub.ExpectArgs{"/sysroot/bin", os.FileMode(0700)}, nil, nil}, {"bindMount", stub.ExpectArgs{"/host/var/lib/planterette/base/debian:f92c9052/usr/bin", "/sysroot/bin", uintptr(0x4005), false}, nil, nil}, | ||||||
| 			{"verbosef", expectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/var/lib/planterette/base/debian:f92c9052/home"), MustAbs("/var/lib/planterette/base/debian:f92c9052/home"), MustAbs("/home"), 0}}}, nil, nil}, {"stat", expectArgs{"/host/var/lib/planterette/base/debian:f92c9052/home"}, isDirFi(true), nil}, {"mkdirAll", expectArgs{"/sysroot/home", os.FileMode(0700)}, nil, nil}, {"bindMount", expectArgs{"/host/var/lib/planterette/base/debian:f92c9052/home", "/sysroot/home", uintptr(0x4005), false}, nil, nil}, | 			{"verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/var/lib/planterette/base/debian:f92c9052/home"), MustAbs("/var/lib/planterette/base/debian:f92c9052/home"), MustAbs("/home"), 0}}}, nil, nil}, {"stat", stub.ExpectArgs{"/host/var/lib/planterette/base/debian:f92c9052/home"}, isDirFi(true), nil}, {"mkdirAll", stub.ExpectArgs{"/sysroot/home", os.FileMode(0700)}, nil, nil}, {"bindMount", stub.ExpectArgs{"/host/var/lib/planterette/base/debian:f92c9052/home", "/sysroot/home", uintptr(0x4005), false}, nil, nil}, | ||||||
| 			{"verbosef", expectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/var/lib/planterette/base/debian:f92c9052/lib64"), MustAbs("/var/lib/planterette/base/debian:f92c9052/lib64"), MustAbs("/lib64"), 0}}}, nil, nil}, {"stat", expectArgs{"/host/var/lib/planterette/base/debian:f92c9052/lib64"}, isDirFi(true), nil}, {"mkdirAll", expectArgs{"/sysroot/lib64", os.FileMode(0700)}, nil, nil}, {"bindMount", expectArgs{"/host/var/lib/planterette/base/debian:f92c9052/lib64", "/sysroot/lib64", uintptr(0x4005), false}, nil, nil}, | 			{"verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/var/lib/planterette/base/debian:f92c9052/lib64"), MustAbs("/var/lib/planterette/base/debian:f92c9052/lib64"), MustAbs("/lib64"), 0}}}, nil, nil}, {"stat", stub.ExpectArgs{"/host/var/lib/planterette/base/debian:f92c9052/lib64"}, isDirFi(true), nil}, {"mkdirAll", stub.ExpectArgs{"/sysroot/lib64", os.FileMode(0700)}, nil, nil}, {"bindMount", stub.ExpectArgs{"/host/var/lib/planterette/base/debian:f92c9052/lib64", "/sysroot/lib64", uintptr(0x4005), false}, nil, nil}, | ||||||
| 			{"verbosef", expectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/var/lib/planterette/base/debian:f92c9052/lost+found"), MustAbs("/var/lib/planterette/base/debian:f92c9052/lost+found"), MustAbs("/lost+found"), 0}}}, nil, nil}, {"stat", expectArgs{"/host/var/lib/planterette/base/debian:f92c9052/lost+found"}, isDirFi(true), nil}, {"mkdirAll", expectArgs{"/sysroot/lost+found", os.FileMode(0700)}, nil, nil}, {"bindMount", expectArgs{"/host/var/lib/planterette/base/debian:f92c9052/lost+found", "/sysroot/lost+found", uintptr(0x4005), false}, nil, nil}, | 			{"verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/var/lib/planterette/base/debian:f92c9052/lost+found"), MustAbs("/var/lib/planterette/base/debian:f92c9052/lost+found"), MustAbs("/lost+found"), 0}}}, nil, nil}, {"stat", stub.ExpectArgs{"/host/var/lib/planterette/base/debian:f92c9052/lost+found"}, isDirFi(true), nil}, {"mkdirAll", stub.ExpectArgs{"/sysroot/lost+found", os.FileMode(0700)}, nil, nil}, {"bindMount", stub.ExpectArgs{"/host/var/lib/planterette/base/debian:f92c9052/lost+found", "/sysroot/lost+found", uintptr(0x4005), false}, nil, nil}, | ||||||
| 			{"verbosef", expectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/var/lib/planterette/base/debian:f92c9052/nix"), MustAbs("/var/lib/planterette/base/debian:f92c9052/nix"), MustAbs("/nix"), 0}}}, nil, nil}, {"stat", expectArgs{"/host/var/lib/planterette/base/debian:f92c9052/nix"}, isDirFi(true), nil}, {"mkdirAll", expectArgs{"/sysroot/nix", os.FileMode(0700)}, nil, nil}, {"bindMount", expectArgs{"/host/var/lib/planterette/base/debian:f92c9052/nix", "/sysroot/nix", uintptr(0x4005), false}, nil, nil}, | 			{"verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/var/lib/planterette/base/debian:f92c9052/nix"), MustAbs("/var/lib/planterette/base/debian:f92c9052/nix"), MustAbs("/nix"), 0}}}, nil, nil}, {"stat", stub.ExpectArgs{"/host/var/lib/planterette/base/debian:f92c9052/nix"}, isDirFi(true), nil}, {"mkdirAll", stub.ExpectArgs{"/sysroot/nix", os.FileMode(0700)}, nil, nil}, {"bindMount", stub.ExpectArgs{"/host/var/lib/planterette/base/debian:f92c9052/nix", "/sysroot/nix", uintptr(0x4005), false}, nil, nil}, | ||||||
| 			{"verbosef", expectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/var/lib/planterette/base/debian:f92c9052/root"), MustAbs("/var/lib/planterette/base/debian:f92c9052/root"), MustAbs("/root"), 0}}}, nil, nil}, {"stat", expectArgs{"/host/var/lib/planterette/base/debian:f92c9052/root"}, isDirFi(true), nil}, {"mkdirAll", expectArgs{"/sysroot/root", os.FileMode(0700)}, nil, nil}, {"bindMount", expectArgs{"/host/var/lib/planterette/base/debian:f92c9052/root", "/sysroot/root", uintptr(0x4005), false}, nil, nil}, | 			{"verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/var/lib/planterette/base/debian:f92c9052/root"), MustAbs("/var/lib/planterette/base/debian:f92c9052/root"), MustAbs("/root"), 0}}}, nil, nil}, {"stat", stub.ExpectArgs{"/host/var/lib/planterette/base/debian:f92c9052/root"}, isDirFi(true), nil}, {"mkdirAll", stub.ExpectArgs{"/sysroot/root", os.FileMode(0700)}, nil, nil}, {"bindMount", stub.ExpectArgs{"/host/var/lib/planterette/base/debian:f92c9052/root", "/sysroot/root", uintptr(0x4005), false}, nil, nil}, | ||||||
| 			{"verbosef", expectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/var/lib/planterette/base/debian:f92c9052/run"), MustAbs("/var/lib/planterette/base/debian:f92c9052/run"), MustAbs("/run"), 0}}}, nil, nil}, {"stat", expectArgs{"/host/var/lib/planterette/base/debian:f92c9052/run"}, isDirFi(true), nil}, {"mkdirAll", expectArgs{"/sysroot/run", os.FileMode(0700)}, nil, nil}, {"bindMount", expectArgs{"/host/var/lib/planterette/base/debian:f92c9052/run", "/sysroot/run", uintptr(0x4005), false}, nil, nil}, | 			{"verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/var/lib/planterette/base/debian:f92c9052/run"), MustAbs("/var/lib/planterette/base/debian:f92c9052/run"), MustAbs("/run"), 0}}}, nil, nil}, {"stat", stub.ExpectArgs{"/host/var/lib/planterette/base/debian:f92c9052/run"}, isDirFi(true), nil}, {"mkdirAll", stub.ExpectArgs{"/sysroot/run", os.FileMode(0700)}, nil, nil}, {"bindMount", stub.ExpectArgs{"/host/var/lib/planterette/base/debian:f92c9052/run", "/sysroot/run", uintptr(0x4005), false}, nil, nil}, | ||||||
| 			{"verbosef", expectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/var/lib/planterette/base/debian:f92c9052/srv"), MustAbs("/var/lib/planterette/base/debian:f92c9052/srv"), MustAbs("/srv"), 0}}}, nil, nil}, {"stat", expectArgs{"/host/var/lib/planterette/base/debian:f92c9052/srv"}, isDirFi(true), nil}, {"mkdirAll", expectArgs{"/sysroot/srv", os.FileMode(0700)}, nil, nil}, {"bindMount", expectArgs{"/host/var/lib/planterette/base/debian:f92c9052/srv", "/sysroot/srv", uintptr(0x4005), false}, nil, nil}, | 			{"verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/var/lib/planterette/base/debian:f92c9052/srv"), MustAbs("/var/lib/planterette/base/debian:f92c9052/srv"), MustAbs("/srv"), 0}}}, nil, nil}, {"stat", stub.ExpectArgs{"/host/var/lib/planterette/base/debian:f92c9052/srv"}, isDirFi(true), nil}, {"mkdirAll", stub.ExpectArgs{"/sysroot/srv", os.FileMode(0700)}, nil, nil}, {"bindMount", stub.ExpectArgs{"/host/var/lib/planterette/base/debian:f92c9052/srv", "/sysroot/srv", uintptr(0x4005), false}, nil, nil}, | ||||||
| 			{"verbosef", expectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/var/lib/planterette/base/debian:f92c9052/sys"), MustAbs("/var/lib/planterette/base/debian:f92c9052/sys"), MustAbs("/sys"), 0}}}, nil, nil}, {"stat", expectArgs{"/host/var/lib/planterette/base/debian:f92c9052/sys"}, isDirFi(true), nil}, {"mkdirAll", expectArgs{"/sysroot/sys", os.FileMode(0700)}, nil, nil}, {"bindMount", expectArgs{"/host/var/lib/planterette/base/debian:f92c9052/sys", "/sysroot/sys", uintptr(0x4005), false}, nil, nil}, | 			{"verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/var/lib/planterette/base/debian:f92c9052/sys"), MustAbs("/var/lib/planterette/base/debian:f92c9052/sys"), MustAbs("/sys"), 0}}}, nil, nil}, {"stat", stub.ExpectArgs{"/host/var/lib/planterette/base/debian:f92c9052/sys"}, isDirFi(true), nil}, {"mkdirAll", stub.ExpectArgs{"/sysroot/sys", os.FileMode(0700)}, nil, nil}, {"bindMount", stub.ExpectArgs{"/host/var/lib/planterette/base/debian:f92c9052/sys", "/sysroot/sys", uintptr(0x4005), false}, nil, nil}, | ||||||
| 			{"verbosef", expectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/var/lib/planterette/base/debian:f92c9052/usr"), MustAbs("/var/lib/planterette/base/debian:f92c9052/usr"), MustAbs("/usr"), 0}}}, nil, nil}, {"stat", expectArgs{"/host/var/lib/planterette/base/debian:f92c9052/usr"}, isDirFi(true), nil}, {"mkdirAll", expectArgs{"/sysroot/usr", os.FileMode(0700)}, nil, nil}, {"bindMount", expectArgs{"/host/var/lib/planterette/base/debian:f92c9052/usr", "/sysroot/usr", uintptr(0x4005), false}, nil, nil}, | 			{"verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/var/lib/planterette/base/debian:f92c9052/usr"), MustAbs("/var/lib/planterette/base/debian:f92c9052/usr"), MustAbs("/usr"), 0}}}, nil, nil}, {"stat", stub.ExpectArgs{"/host/var/lib/planterette/base/debian:f92c9052/usr"}, isDirFi(true), nil}, {"mkdirAll", stub.ExpectArgs{"/sysroot/usr", os.FileMode(0700)}, nil, nil}, {"bindMount", stub.ExpectArgs{"/host/var/lib/planterette/base/debian:f92c9052/usr", "/sysroot/usr", uintptr(0x4005), false}, nil, nil}, | ||||||
| 			{"verbosef", expectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/var/lib/planterette/base/debian:f92c9052/var"), MustAbs("/var/lib/planterette/base/debian:f92c9052/var"), MustAbs("/var"), 0}}}, nil, nil}, {"stat", expectArgs{"/host/var/lib/planterette/base/debian:f92c9052/var"}, isDirFi(true), nil}, {"mkdirAll", expectArgs{"/sysroot/var", os.FileMode(0700)}, nil, nil}, {"bindMount", expectArgs{"/host/var/lib/planterette/base/debian:f92c9052/var", "/sysroot/var", uintptr(0x4005), false}, nil, nil}, | 			{"verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &BindMountOp{MustAbs("/var/lib/planterette/base/debian:f92c9052/var"), MustAbs("/var/lib/planterette/base/debian:f92c9052/var"), MustAbs("/var"), 0}}}, nil, nil}, {"stat", stub.ExpectArgs{"/host/var/lib/planterette/base/debian:f92c9052/var"}, isDirFi(true), nil}, {"mkdirAll", stub.ExpectArgs{"/sysroot/var", os.FileMode(0700)}, nil, nil}, {"bindMount", stub.ExpectArgs{"/host/var/lib/planterette/base/debian:f92c9052/var", "/sysroot/var", uintptr(0x4005), false}, nil, nil}, | ||||||
| 		}, nil}, | 		}, nil}, | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -2,7 +2,6 @@ package container | |||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"bytes" | 	"bytes" | ||||||
| 	"errors" |  | ||||||
| 	"io" | 	"io" | ||||||
| 	"io/fs" | 	"io/fs" | ||||||
| 	"os" | 	"os" | ||||||
| @ -11,16 +10,14 @@ import ( | |||||||
| 	"runtime" | 	"runtime" | ||||||
| 	"slices" | 	"slices" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"sync" |  | ||||||
| 	"syscall" | 	"syscall" | ||||||
| 	"testing" | 	"testing" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
| 	"hakurei.app/container/seccomp" | 	"hakurei.app/container/seccomp" | ||||||
|  | 	"hakurei.app/container/stub" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var errUnique = errors.New("unique error injected by the test suite") |  | ||||||
| 
 |  | ||||||
| type opValidTestCase struct { | type opValidTestCase struct { | ||||||
| 	name string | 	name string | ||||||
| 	op   Op | 	op   Op | ||||||
| @ -28,9 +25,15 @@ type opValidTestCase struct { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func checkOpsValid(t *testing.T, testCases []opValidTestCase) { | func checkOpsValid(t *testing.T, testCases []opValidTestCase) { | ||||||
|  | 	t.Helper() | ||||||
|  | 
 | ||||||
| 	t.Run("valid", func(t *testing.T) { | 	t.Run("valid", func(t *testing.T) { | ||||||
|  | 		t.Helper() | ||||||
|  | 
 | ||||||
| 		for _, tc := range testCases { | 		for _, tc := range testCases { | ||||||
| 			t.Run(tc.name, func(t *testing.T) { | 			t.Run(tc.name, func(t *testing.T) { | ||||||
|  | 				t.Helper() | ||||||
|  | 
 | ||||||
| 				if got := tc.op.Valid(); got != tc.want { | 				if got := tc.op.Valid(); got != tc.want { | ||||||
| 					t.Errorf("Valid: %v, want %v", got, tc.want) | 					t.Errorf("Valid: %v, want %v", got, tc.want) | ||||||
| 				} | 				} | ||||||
| @ -46,9 +49,15 @@ type opsBuilderTestCase struct { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func checkOpsBuilder(t *testing.T, testCases []opsBuilderTestCase) { | func checkOpsBuilder(t *testing.T, testCases []opsBuilderTestCase) { | ||||||
|  | 	t.Helper() | ||||||
|  | 
 | ||||||
| 	t.Run("build", func(t *testing.T) { | 	t.Run("build", func(t *testing.T) { | ||||||
|  | 		t.Helper() | ||||||
|  | 
 | ||||||
| 		for _, tc := range testCases { | 		for _, tc := range testCases { | ||||||
| 			t.Run(tc.name, func(t *testing.T) { | 			t.Run(tc.name, func(t *testing.T) { | ||||||
|  | 				t.Helper() | ||||||
|  | 
 | ||||||
| 				if !slices.EqualFunc(*tc.ops, tc.want, func(op Op, v Op) bool { return op.Is(v) }) { | 				if !slices.EqualFunc(*tc.ops, tc.want, func(op Op, v Op) bool { return op.Is(v) }) { | ||||||
| 					t.Errorf("Ops: %#v, want %#v", tc.ops, tc.want) | 					t.Errorf("Ops: %#v, want %#v", tc.ops, tc.want) | ||||||
| 				} | 				} | ||||||
| @ -64,9 +73,15 @@ type opIsTestCase struct { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func checkOpIs(t *testing.T, testCases []opIsTestCase) { | func checkOpIs(t *testing.T, testCases []opIsTestCase) { | ||||||
|  | 	t.Helper() | ||||||
|  | 
 | ||||||
| 	t.Run("is", func(t *testing.T) { | 	t.Run("is", func(t *testing.T) { | ||||||
|  | 		t.Helper() | ||||||
|  | 
 | ||||||
| 		for _, tc := range testCases { | 		for _, tc := range testCases { | ||||||
| 			t.Run(tc.name, func(t *testing.T) { | 			t.Run(tc.name, func(t *testing.T) { | ||||||
|  | 				t.Helper() | ||||||
|  | 
 | ||||||
| 				if got := tc.op.Is(tc.v); got != tc.want { | 				if got := tc.op.Is(tc.v); got != tc.want { | ||||||
| 					t.Errorf("Is: %v, want %v", got, tc.want) | 					t.Errorf("Is: %v, want %v", got, tc.want) | ||||||
| 				} | 				} | ||||||
| @ -84,16 +99,26 @@ type opMetaTestCase struct { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func checkOpMeta(t *testing.T, testCases []opMetaTestCase) { | func checkOpMeta(t *testing.T, testCases []opMetaTestCase) { | ||||||
|  | 	t.Helper() | ||||||
|  | 
 | ||||||
| 	t.Run("meta", func(t *testing.T) { | 	t.Run("meta", func(t *testing.T) { | ||||||
|  | 		t.Helper() | ||||||
|  | 
 | ||||||
| 		for _, tc := range testCases { | 		for _, tc := range testCases { | ||||||
| 			t.Run(tc.name, func(t *testing.T) { | 			t.Run(tc.name, func(t *testing.T) { | ||||||
|  | 				t.Helper() | ||||||
|  | 
 | ||||||
| 				t.Run("prefix", func(t *testing.T) { | 				t.Run("prefix", func(t *testing.T) { | ||||||
|  | 					t.Helper() | ||||||
|  | 
 | ||||||
| 					if got := tc.op.prefix(); got != tc.wantPrefix { | 					if got := tc.op.prefix(); got != tc.wantPrefix { | ||||||
| 						t.Errorf("prefix: %q, want %q", got, tc.wantPrefix) | 						t.Errorf("prefix: %q, want %q", got, tc.wantPrefix) | ||||||
| 					} | 					} | ||||||
| 				}) | 				}) | ||||||
| 
 | 
 | ||||||
| 				t.Run("string", func(t *testing.T) { | 				t.Run("string", func(t *testing.T) { | ||||||
|  | 					t.Helper() | ||||||
|  | 
 | ||||||
| 					if got := tc.op.String(); got != tc.wantString { | 					if got := tc.op.String(); got != tc.wantString { | ||||||
| 						t.Errorf("String: %s, want %s", got, tc.wantString) | 						t.Errorf("String: %s, want %s", got, tc.wantString) | ||||||
| 					} | 					} | ||||||
| @ -106,20 +131,26 @@ func checkOpMeta(t *testing.T, testCases []opMetaTestCase) { | |||||||
| type simpleTestCase struct { | type simpleTestCase struct { | ||||||
| 	name    string | 	name    string | ||||||
| 	f       func(k syscallDispatcher) error | 	f       func(k syscallDispatcher) error | ||||||
| 	want    [][]kexpect | 	want    stub.Expect | ||||||
| 	wantErr error | 	wantErr error | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func checkSimple(t *testing.T, fname string, testCases []simpleTestCase) { | func checkSimple(t *testing.T, fname string, testCases []simpleTestCase) { | ||||||
|  | 	t.Helper() | ||||||
|  | 
 | ||||||
| 	for _, tc := range testCases { | 	for _, tc := range testCases { | ||||||
| 		t.Run(tc.name, func(t *testing.T) { | 		t.Run(tc.name, func(t *testing.T) { | ||||||
| 			defer handleExitStub() | 			t.Helper() | ||||||
| 			k := &kstub{t: t, want: tc.want, wg: new(sync.WaitGroup)} | 
 | ||||||
|  | 			defer stub.HandleExit() | ||||||
|  | 			k := &kstub{stub.New(t, func(s *stub.Stub[syscallDispatcher]) syscallDispatcher { return &kstub{s} }, tc.want)} | ||||||
| 			if err := tc.f(k); !reflect.DeepEqual(err, tc.wantErr) { | 			if err := tc.f(k); !reflect.DeepEqual(err, tc.wantErr) { | ||||||
| 				t.Errorf("%s: error = %v, want %v", fname, err, tc.wantErr) | 				t.Errorf("%s: error = %v, want %v", fname, err, tc.wantErr) | ||||||
| 			} | 			} | ||||||
| 			k.handleIncomplete(func(k *kstub) { | 			k.VisitIncomplete(func(s *stub.Stub[syscallDispatcher]) { | ||||||
| 				t.Errorf("%s: %d calls, want %d (track %d)", fname, k.pos, len(k.want[k.track]), k.track) | 				t.Helper() | ||||||
|  | 
 | ||||||
|  | 				t.Errorf("%s: %d calls, want %d", fname, s.Pos(), s.Len()) | ||||||
| 			}) | 			}) | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
| @ -130,22 +161,31 @@ type opBehaviourTestCase struct { | |||||||
| 	params *Params | 	params *Params | ||||||
| 	op     Op | 	op     Op | ||||||
| 
 | 
 | ||||||
| 	early        []kexpect | 	early        []stub.Call | ||||||
| 	wantErrEarly error | 	wantErrEarly error | ||||||
| 
 | 
 | ||||||
| 	apply        []kexpect | 	apply        []stub.Call | ||||||
| 	wantErrApply error | 	wantErrApply error | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func checkOpBehaviour(t *testing.T, testCases []opBehaviourTestCase) { | func checkOpBehaviour(t *testing.T, testCases []opBehaviourTestCase) { | ||||||
|  | 	t.Helper() | ||||||
|  | 
 | ||||||
| 	t.Run("behaviour", func(t *testing.T) { | 	t.Run("behaviour", func(t *testing.T) { | ||||||
|  | 		t.Helper() | ||||||
|  | 
 | ||||||
| 		for _, tc := range testCases { | 		for _, tc := range testCases { | ||||||
| 			t.Run(tc.name, func(t *testing.T) { | 			t.Run(tc.name, func(t *testing.T) { | ||||||
| 				defer handleExitStub() | 				t.Helper() | ||||||
|  | 
 | ||||||
|  | 				defer stub.HandleExit() | ||||||
| 				state := &setupState{Params: tc.params} | 				state := &setupState{Params: tc.params} | ||||||
| 				k := &kstub{t: t, want: [][]kexpect{slices.Concat(tc.early, []kexpect{{name: "\x00"}}, tc.apply)}, wg: new(sync.WaitGroup)} | 				k := &kstub{stub.New(t, | ||||||
|  | 					func(s *stub.Stub[syscallDispatcher]) syscallDispatcher { return &kstub{s} }, | ||||||
|  | 					stub.Expect{Calls: slices.Concat(tc.early, []stub.Call{{Name: stub.CallSeparator}}, tc.apply)}, | ||||||
|  | 				)} | ||||||
| 				errEarly := tc.op.early(state, k) | 				errEarly := tc.op.early(state, k) | ||||||
| 				k.expect("\x00") | 				k.Expects(stub.CallSeparator) | ||||||
| 				if !reflect.DeepEqual(errEarly, tc.wantErrEarly) { | 				if !reflect.DeepEqual(errEarly, tc.wantErrEarly) { | ||||||
| 					t.Errorf("early: error = %v, want %v", errEarly, tc.wantErrEarly) | 					t.Errorf("early: error = %v, want %v", errEarly, tc.wantErrEarly) | ||||||
| 				} | 				} | ||||||
| @ -158,8 +198,8 @@ func checkOpBehaviour(t *testing.T, testCases []opBehaviourTestCase) { | |||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
| 			out: | 			out: | ||||||
| 				k.handleIncomplete(func(k *kstub) { | 				k.VisitIncomplete(func(s *stub.Stub[syscallDispatcher]) { | ||||||
| 					count := k.pos - 1 // separator | 					count := k.Pos() - 1 // separator | ||||||
| 					if count < len(tc.early) { | 					if count < len(tc.early) { | ||||||
| 						t.Errorf("early: %d calls, want %d", count, len(tc.early)) | 						t.Errorf("early: %d calls, want %d", count, len(tc.early)) | ||||||
| 					} else { | 					} else { | ||||||
| @ -226,8 +266,6 @@ func (writeErrOsFile) Stat() (fs.FileInfo, error)  { panic("unreachable") } | |||||||
| func (writeErrOsFile) Read([]byte) (int, error)    { panic("unreachable") } | func (writeErrOsFile) Read([]byte) (int, error)    { panic("unreachable") } | ||||||
| func (writeErrOsFile) Close() error                { panic("unreachable") } | func (writeErrOsFile) Close() error                { panic("unreachable") } | ||||||
| 
 | 
 | ||||||
| type expectArgs = [5]any |  | ||||||
| 
 |  | ||||||
| type isDirFi bool | type isDirFi bool | ||||||
| 
 | 
 | ||||||
| func (isDirFi) Name() string       { panic("unreachable") } | func (isDirFi) Name() string       { panic("unreachable") } | ||||||
| @ -252,184 +290,83 @@ func (nameDentry) IsDir() bool                { panic("unreachable") } | |||||||
| func (nameDentry) Type() fs.FileMode          { panic("unreachable") } | func (nameDentry) Type() fs.FileMode          { panic("unreachable") } | ||||||
| func (nameDentry) Info() (fs.FileInfo, error) { panic("unreachable") } | func (nameDentry) Info() (fs.FileInfo, error) { panic("unreachable") } | ||||||
| 
 | 
 | ||||||
| type kexpect struct { | type kstub struct{ *stub.Stub[syscallDispatcher] } | ||||||
| 	name string |  | ||||||
| 	args expectArgs |  | ||||||
| 	ret  any |  | ||||||
| 	err  error |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| func (k *kexpect) error(ok ...bool) error { | func (k *kstub) new(f func(k syscallDispatcher)) { k.Helper(); k.New(f) } | ||||||
| 	if !slices.Contains(ok, false) { |  | ||||||
| 		return k.err |  | ||||||
| 	} |  | ||||||
| 	return syscall.ENOTRECOVERABLE |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| func handleExitStub() { | func (k *kstub) lockOSThread() { k.Helper(); k.Expects("lockOSThread") } | ||||||
| 	r := recover() |  | ||||||
| 	if r == 0xdeadbeef { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	if r != nil { |  | ||||||
| 		panic(r) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type kstub struct { |  | ||||||
| 	t *testing.T |  | ||||||
| 
 |  | ||||||
| 	want [][]kexpect |  | ||||||
| 	// pos is the current position in want[track]. |  | ||||||
| 	pos int |  | ||||||
| 	// track is the current active want. |  | ||||||
| 	track int |  | ||||||
| 	// sub stores addresses of kstub created by new. |  | ||||||
| 	sub []*kstub |  | ||||||
| 	// wg waits for all descendants to complete. |  | ||||||
| 	wg *sync.WaitGroup |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // handleIncomplete calls f on an incomplete k and all its descendants. |  | ||||||
| func (k *kstub) handleIncomplete(f func(k *kstub)) { |  | ||||||
| 	k.wg.Wait() |  | ||||||
| 
 |  | ||||||
| 	if k.want != nil && len(k.want[k.track]) != k.pos { |  | ||||||
| 		f(k) |  | ||||||
| 	} |  | ||||||
| 	for _, sk := range k.sub { |  | ||||||
| 		sk.handleIncomplete(f) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // expect checks name and returns the current kexpect and advances pos. |  | ||||||
| func (k *kstub) expect(name string) (expect *kexpect) { |  | ||||||
| 	if len(k.want[k.track]) == k.pos { |  | ||||||
| 		k.t.Fatal("expect: want too short") |  | ||||||
| 	} |  | ||||||
| 	expect = &k.want[k.track][k.pos] |  | ||||||
| 	if name != expect.name { |  | ||||||
| 		if expect.name == "\x00" { |  | ||||||
| 			k.t.Fatalf("expect: func = %s, separator overrun", name) |  | ||||||
| 		} |  | ||||||
| 		if name == "\x00" { |  | ||||||
| 			k.t.Fatalf("expect: separator, want %s", expect.name) |  | ||||||
| 		} |  | ||||||
| 		k.t.Fatalf("expect: func = %s, want %s", name, expect.name) |  | ||||||
| 	} |  | ||||||
| 	k.pos++ |  | ||||||
| 	return |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // checkArg checks an argument comparable with the == operator. Avoid using this with pointers. |  | ||||||
| func checkArg[T comparable](k *kstub, arg string, got T, n int) bool { |  | ||||||
| 	if k.pos == 0 { |  | ||||||
| 		panic("invalid call to checkArg") |  | ||||||
| 	} |  | ||||||
| 	expect := k.want[k.track][k.pos-1] |  | ||||||
| 	want, ok := expect.args[n].(T) |  | ||||||
| 	if !ok || got != want { |  | ||||||
| 		k.t.Errorf("%s: %s = %#v, want %#v (%d)", expect.name, arg, got, want, k.pos-1) |  | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
| 	return true |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // checkArgReflect checks an argument of any type. |  | ||||||
| func checkArgReflect(k *kstub, arg string, got any, n int) bool { |  | ||||||
| 	if k.pos == 0 { |  | ||||||
| 		panic("invalid call to checkArgReflect") |  | ||||||
| 	} |  | ||||||
| 	expect := k.want[k.track][k.pos-1] |  | ||||||
| 	want := expect.args[n] |  | ||||||
| 	if !reflect.DeepEqual(got, want) { |  | ||||||
| 		k.t.Errorf("%s: %s = %#v, want %#v (%d)", expect.name, arg, got, want, k.pos-1) |  | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
| 	return true |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (k *kstub) new(f func(k syscallDispatcher)) { |  | ||||||
| 	k.expect("new") |  | ||||||
| 	if len(k.want) <= k.track+1 { |  | ||||||
| 		k.t.Fatalf("new: track overrun") |  | ||||||
| 	} |  | ||||||
| 	sk := &kstub{t: k.t, want: k.want, track: len(k.sub) + 1, wg: k.wg} |  | ||||||
| 	k.sub = append(k.sub, sk) |  | ||||||
| 	k.wg.Add(1) |  | ||||||
| 	go func() { |  | ||||||
| 		defer k.wg.Done() |  | ||||||
| 		defer handleExitStub() |  | ||||||
| 		f(sk) |  | ||||||
| 	}() |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (k *kstub) lockOSThread() { k.expect("lockOSThread") } |  | ||||||
| 
 | 
 | ||||||
| func (k *kstub) setPtracer(pid uintptr) error { | func (k *kstub) setPtracer(pid uintptr) error { | ||||||
| 	return k.expect("setPtracer").error( | 	k.Helper() | ||||||
| 		checkArg(k, "pid", pid, 0)) | 	return k.Expects("setPtracer").Error( | ||||||
|  | 		stub.CheckArg(k.Stub, "pid", pid, 0)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) setDumpable(dumpable uintptr) error { | func (k *kstub) setDumpable(dumpable uintptr) error { | ||||||
| 	return k.expect("setDumpable").error( | 	k.Helper() | ||||||
| 		checkArg(k, "dumpable", dumpable, 0)) | 	return k.Expects("setDumpable").Error( | ||||||
|  | 		stub.CheckArg(k.Stub, "dumpable", dumpable, 0)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) setNoNewPrivs() error { return k.expect("setNoNewPrivs").err } | func (k *kstub) setNoNewPrivs() error { k.Helper(); return k.Expects("setNoNewPrivs").Err } | ||||||
| func (k *kstub) lastcap() uintptr     { return k.expect("lastcap").ret.(uintptr) } | func (k *kstub) lastcap() uintptr     { k.Helper(); return k.Expects("lastcap").Ret.(uintptr) } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) capset(hdrp *capHeader, datap *[2]capData) error { | func (k *kstub) capset(hdrp *capHeader, datap *[2]capData) error { | ||||||
| 	return k.expect("capset").error( | 	k.Helper() | ||||||
| 		checkArgReflect(k, "hdrp", hdrp, 0), | 	return k.Expects("capset").Error( | ||||||
| 		checkArgReflect(k, "datap", datap, 1)) | 		stub.CheckArgReflect(k.Stub, "hdrp", hdrp, 0), | ||||||
|  | 		stub.CheckArgReflect(k.Stub, "datap", datap, 1)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) capBoundingSetDrop(cap uintptr) error { | func (k *kstub) capBoundingSetDrop(cap uintptr) error { | ||||||
| 	return k.expect("capBoundingSetDrop").error( | 	k.Helper() | ||||||
| 		checkArg(k, "cap", cap, 0)) | 	return k.Expects("capBoundingSetDrop").Error( | ||||||
|  | 		stub.CheckArg(k.Stub, "cap", cap, 0)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) capAmbientClearAll() error { return k.expect("capAmbientClearAll").err } | func (k *kstub) capAmbientClearAll() error { k.Helper(); return k.Expects("capAmbientClearAll").Err } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) capAmbientRaise(cap uintptr) error { | func (k *kstub) capAmbientRaise(cap uintptr) error { | ||||||
| 	return k.expect("capAmbientRaise").error( | 	k.Helper() | ||||||
| 		checkArg(k, "cap", cap, 0)) | 	return k.Expects("capAmbientRaise").Error( | ||||||
|  | 		stub.CheckArg(k.Stub, "cap", cap, 0)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) isatty(fd int) bool { | func (k *kstub) isatty(fd int) bool { | ||||||
| 	expect := k.expect("isatty") | 	k.Helper() | ||||||
| 	if !checkArg(k, "fd", fd, 0) { | 	expect := k.Expects("isatty") | ||||||
| 		k.t.FailNow() | 	if !stub.CheckArg(k.Stub, "fd", fd, 0) { | ||||||
|  | 		k.FailNow() | ||||||
| 	} | 	} | ||||||
| 	return expect.ret.(bool) | 	return expect.Ret.(bool) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) receive(key string, e any, fdp *uintptr) (closeFunc func() error, err error) { | func (k *kstub) receive(key string, e any, fdp *uintptr) (closeFunc func() error, err error) { | ||||||
| 	expect := k.expect("receive") | 	k.Helper() | ||||||
|  | 	expect := k.Expects("receive") | ||||||
| 
 | 
 | ||||||
| 	var closed bool | 	var closed bool | ||||||
| 	closeFunc = func() error { | 	closeFunc = func() error { | ||||||
| 		if closed { | 		if closed { | ||||||
| 			k.t.Error("closeFunc called more than once") | 			k.Error("closeFunc called more than once") | ||||||
| 			return os.ErrClosed | 			return os.ErrClosed | ||||||
| 		} | 		} | ||||||
| 		closed = true | 		closed = true | ||||||
| 
 | 
 | ||||||
| 		if expect.ret != nil { | 		if expect.Ret != nil { | ||||||
| 			// use return stored in kexpect for closeFunc instead | 			// use return stored in kexpect for closeFunc instead | ||||||
| 			return expect.ret.(error) | 			return expect.Ret.(error) | ||||||
| 		} | 		} | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| 	err = expect.error( | 	err = expect.Error( | ||||||
| 		checkArg(k, "key", key, 0), | 		stub.CheckArg(k.Stub, "key", key, 0), | ||||||
| 		checkArgReflect(k, "e", e, 1), | 		stub.CheckArgReflect(k.Stub, "e", e, 1), | ||||||
| 		checkArgReflect(k, "fdp", fdp, 2)) | 		stub.CheckArgReflect(k.Stub, "fdp", fdp, 2)) | ||||||
| 
 | 
 | ||||||
| 	// 3 is unused so stores params | 	// 3 is unused so stores params | ||||||
| 	if expect.args[3] != nil { | 	if expect.Args[3] != nil { | ||||||
| 		if v, ok := expect.args[3].(*initParams); ok && v != nil { | 		if v, ok := expect.Args[3].(*initParams); ok && v != nil { | ||||||
| 			if p, ok0 := e.(*initParams); ok0 && p != nil { | 			if p, ok0 := e.(*initParams); ok0 && p != nil { | ||||||
| 				*p = *v | 				*p = *v | ||||||
| 			} | 			} | ||||||
| @ -437,8 +374,8 @@ func (k *kstub) receive(key string, e any, fdp *uintptr) (closeFunc func() error | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// 4 is unused so stores fd | 	// 4 is unused so stores fd | ||||||
| 	if expect.args[4] != nil { | 	if expect.Args[4] != nil { | ||||||
| 		if v, ok := expect.args[4].(uintptr); ok && v >= 3 { | 		if v, ok := expect.Args[4].(uintptr); ok && v >= 3 { | ||||||
| 			if fdp != nil { | 			if fdp != nil { | ||||||
| 				*fdp = v | 				*fdp = v | ||||||
| 			} | 			} | ||||||
| @ -449,246 +386,277 @@ func (k *kstub) receive(key string, e any, fdp *uintptr) (closeFunc func() error | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) bindMount(source, target string, flags uintptr, eq bool) error { | func (k *kstub) bindMount(source, target string, flags uintptr, eq bool) error { | ||||||
| 	return k.expect("bindMount").error( | 	k.Helper() | ||||||
| 		checkArg(k, "source", source, 0), | 	return k.Expects("bindMount").Error( | ||||||
| 		checkArg(k, "target", target, 1), | 		stub.CheckArg(k.Stub, "source", source, 0), | ||||||
| 		checkArg(k, "flags", flags, 2), | 		stub.CheckArg(k.Stub, "target", target, 1), | ||||||
| 		checkArg(k, "eq", eq, 3)) | 		stub.CheckArg(k.Stub, "flags", flags, 2), | ||||||
|  | 		stub.CheckArg(k.Stub, "eq", eq, 3)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) remount(target string, flags uintptr) error { | func (k *kstub) remount(target string, flags uintptr) error { | ||||||
| 	return k.expect("remount").error( | 	k.Helper() | ||||||
| 		checkArg(k, "target", target, 0), | 	return k.Expects("remount").Error( | ||||||
| 		checkArg(k, "flags", flags, 1)) | 		stub.CheckArg(k.Stub, "target", target, 0), | ||||||
|  | 		stub.CheckArg(k.Stub, "flags", flags, 1)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) mountTmpfs(fsname, target string, flags uintptr, size int, perm os.FileMode) error { | func (k *kstub) mountTmpfs(fsname, target string, flags uintptr, size int, perm os.FileMode) error { | ||||||
| 	return k.expect("mountTmpfs").error( | 	k.Helper() | ||||||
| 		checkArg(k, "fsname", fsname, 0), | 	return k.Expects("mountTmpfs").Error( | ||||||
| 		checkArg(k, "target", target, 1), | 		stub.CheckArg(k.Stub, "fsname", fsname, 0), | ||||||
| 		checkArg(k, "flags", flags, 2), | 		stub.CheckArg(k.Stub, "target", target, 1), | ||||||
| 		checkArg(k, "size", size, 3), | 		stub.CheckArg(k.Stub, "flags", flags, 2), | ||||||
| 		checkArg(k, "perm", perm, 4)) | 		stub.CheckArg(k.Stub, "size", size, 3), | ||||||
|  | 		stub.CheckArg(k.Stub, "perm", perm, 4)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) ensureFile(name string, perm, pperm os.FileMode) error { | func (k *kstub) ensureFile(name string, perm, pperm os.FileMode) error { | ||||||
| 
 | 	k.Helper() | ||||||
| 	return k.expect("ensureFile").error( | 	return k.Expects("ensureFile").Error( | ||||||
| 		checkArg(k, "name", name, 0), | 		stub.CheckArg(k.Stub, "name", name, 0), | ||||||
| 		checkArg(k, "perm", perm, 1), | 		stub.CheckArg(k.Stub, "perm", perm, 1), | ||||||
| 		checkArg(k, "pperm", pperm, 2)) | 		stub.CheckArg(k.Stub, "pperm", pperm, 2)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) seccompLoad(rules []seccomp.NativeRule, flags seccomp.ExportFlag) error { | func (k *kstub) seccompLoad(rules []seccomp.NativeRule, flags seccomp.ExportFlag) error { | ||||||
| 	return k.expect("seccompLoad").error( | 	k.Helper() | ||||||
| 		checkArgReflect(k, "rules", rules, 0), | 	return k.Expects("seccompLoad").Error( | ||||||
| 		checkArg(k, "flags", flags, 1)) | 		stub.CheckArgReflect(k.Stub, "rules", rules, 0), | ||||||
|  | 		stub.CheckArg(k.Stub, "flags", flags, 1)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) notify(c chan<- os.Signal, sig ...os.Signal) { | func (k *kstub) notify(c chan<- os.Signal, sig ...os.Signal) { | ||||||
| 	expect := k.expect("notify") | 	k.Helper() | ||||||
| 	if c == nil || expect.error( | 	expect := k.Expects("notify") | ||||||
| 		checkArgReflect(k, "sig", sig, 1)) != nil { | 	if c == nil || expect.Error( | ||||||
| 		k.t.FailNow() | 		stub.CheckArgReflect(k.Stub, "sig", sig, 1)) != nil { | ||||||
|  | 		k.FailNow() | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// export channel for external instrumentation | 	// export channel for external instrumentation | ||||||
| 	if chanf, ok := expect.args[0].(func(c chan<- os.Signal)); ok && chanf != nil { | 	if chanf, ok := expect.Args[0].(func(c chan<- os.Signal)); ok && chanf != nil { | ||||||
| 		chanf(c) | 		chanf(c) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) start(c *exec.Cmd) error { | func (k *kstub) start(c *exec.Cmd) error { | ||||||
| 	expect := k.expect("start") | 	k.Helper() | ||||||
| 	err := expect.error( | 	expect := k.Expects("start") | ||||||
| 		checkArg(k, "c.Path", c.Path, 0), | 	err := expect.Error( | ||||||
| 		checkArgReflect(k, "c.Args", c.Args, 1), | 		stub.CheckArg(k.Stub, "c.Path", c.Path, 0), | ||||||
| 		checkArgReflect(k, "c.Env", c.Env, 2), | 		stub.CheckArgReflect(k.Stub, "c.Args", c.Args, 1), | ||||||
| 		checkArg(k, "c.Dir", c.Dir, 3)) | 		stub.CheckArgReflect(k.Stub, "c.Env", c.Env, 2), | ||||||
|  | 		stub.CheckArg(k.Stub, "c.Dir", c.Dir, 3)) | ||||||
| 
 | 
 | ||||||
| 	if process, ok := expect.ret.(*os.Process); ok && process != nil { | 	if process, ok := expect.Ret.(*os.Process); ok && process != nil { | ||||||
| 		c.Process = process | 		c.Process = process | ||||||
| 	} | 	} | ||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) signal(c *exec.Cmd, sig os.Signal) error { | func (k *kstub) signal(c *exec.Cmd, sig os.Signal) error { | ||||||
| 	return k.expect("signal").error( | 	k.Helper() | ||||||
| 		checkArg(k, "c.Path", c.Path, 0), | 	return k.Expects("signal").Error( | ||||||
| 		checkArgReflect(k, "c.Args", c.Args, 1), | 		stub.CheckArg(k.Stub, "c.Path", c.Path, 0), | ||||||
| 		checkArgReflect(k, "c.Env", c.Env, 2), | 		stub.CheckArgReflect(k.Stub, "c.Args", c.Args, 1), | ||||||
| 		checkArg(k, "c.Dir", c.Dir, 3), | 		stub.CheckArgReflect(k.Stub, "c.Env", c.Env, 2), | ||||||
| 		checkArg(k, "sig", sig, 4)) | 		stub.CheckArg(k.Stub, "c.Dir", c.Dir, 3), | ||||||
|  | 		stub.CheckArg(k.Stub, "sig", sig, 4)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) evalSymlinks(path string) (string, error) { | func (k *kstub) evalSymlinks(path string) (string, error) { | ||||||
| 	expect := k.expect("evalSymlinks") | 	k.Helper() | ||||||
| 	return expect.ret.(string), expect.error( | 	expect := k.Expects("evalSymlinks") | ||||||
| 		checkArg(k, "path", path, 0)) | 	return expect.Ret.(string), expect.Error( | ||||||
|  | 		stub.CheckArg(k.Stub, "path", path, 0)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) exit(code int) { | func (k *kstub) exit(code int) { | ||||||
| 	k.expect("exit") | 	k.Helper() | ||||||
| 	if !checkArg(k, "code", code, 0) { | 	k.Expects("exit") | ||||||
| 		k.t.FailNow() | 	if !stub.CheckArg(k.Stub, "code", code, 0) { | ||||||
|  | 		k.FailNow() | ||||||
| 	} | 	} | ||||||
| 	panic(0xdeadbeef) | 	panic(stub.PanicExit) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) getpid() int { return k.expect("getpid").ret.(int) } | func (k *kstub) getpid() int { k.Helper(); return k.Expects("getpid").Ret.(int) } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) stat(name string) (os.FileInfo, error) { | func (k *kstub) stat(name string) (os.FileInfo, error) { | ||||||
| 	expect := k.expect("stat") | 	k.Helper() | ||||||
| 	return expect.ret.(os.FileInfo), expect.error( | 	expect := k.Expects("stat") | ||||||
| 		checkArg(k, "name", name, 0)) | 	return expect.Ret.(os.FileInfo), expect.Error( | ||||||
|  | 		stub.CheckArg(k.Stub, "name", name, 0)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) mkdir(name string, perm os.FileMode) error { | func (k *kstub) mkdir(name string, perm os.FileMode) error { | ||||||
| 	return k.expect("mkdir").error( | 	k.Helper() | ||||||
| 		checkArg(k, "name", name, 0), | 	return k.Expects("mkdir").Error( | ||||||
| 		checkArg(k, "perm", perm, 1)) | 		stub.CheckArg(k.Stub, "name", name, 0), | ||||||
|  | 		stub.CheckArg(k.Stub, "perm", perm, 1)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) mkdirTemp(dir, pattern string) (string, error) { | func (k *kstub) mkdirTemp(dir, pattern string) (string, error) { | ||||||
| 	expect := k.expect("mkdirTemp") | 	k.Helper() | ||||||
| 	return expect.ret.(string), expect.error( | 	expect := k.Expects("mkdirTemp") | ||||||
| 		checkArg(k, "dir", dir, 0), | 	return expect.Ret.(string), expect.Error( | ||||||
| 		checkArg(k, "pattern", pattern, 1)) | 		stub.CheckArg(k.Stub, "dir", dir, 0), | ||||||
|  | 		stub.CheckArg(k.Stub, "pattern", pattern, 1)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) mkdirAll(path string, perm os.FileMode) error { | func (k *kstub) mkdirAll(path string, perm os.FileMode) error { | ||||||
| 	return k.expect("mkdirAll").error( | 	k.Helper() | ||||||
| 		checkArg(k, "path", path, 0), | 	return k.Expects("mkdirAll").Error( | ||||||
| 		checkArg(k, "perm", perm, 1)) | 		stub.CheckArg(k.Stub, "path", path, 0), | ||||||
|  | 		stub.CheckArg(k.Stub, "perm", perm, 1)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) readdir(name string) ([]os.DirEntry, error) { | func (k *kstub) readdir(name string) ([]os.DirEntry, error) { | ||||||
| 	expect := k.expect("readdir") | 	k.Helper() | ||||||
| 	return expect.ret.([]os.DirEntry), expect.error( | 	expect := k.Expects("readdir") | ||||||
| 		checkArg(k, "name", name, 0)) | 	return expect.Ret.([]os.DirEntry), expect.Error( | ||||||
|  | 		stub.CheckArg(k.Stub, "name", name, 0)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) openNew(name string) (osFile, error) { | func (k *kstub) openNew(name string) (osFile, error) { | ||||||
| 	expect := k.expect("openNew") | 	k.Helper() | ||||||
| 	return expect.ret.(osFile), expect.error( | 	expect := k.Expects("openNew") | ||||||
| 		checkArg(k, "name", name, 0)) | 	return expect.Ret.(osFile), expect.Error( | ||||||
|  | 		stub.CheckArg(k.Stub, "name", name, 0)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) writeFile(name string, data []byte, perm os.FileMode) error { | func (k *kstub) writeFile(name string, data []byte, perm os.FileMode) error { | ||||||
| 	return k.expect("writeFile").error( | 	k.Helper() | ||||||
| 		checkArg(k, "name", name, 0), | 	return k.Expects("writeFile").Error( | ||||||
| 		checkArgReflect(k, "data", data, 1), | 		stub.CheckArg(k.Stub, "name", name, 0), | ||||||
| 		checkArg(k, "perm", perm, 2)) | 		stub.CheckArgReflect(k.Stub, "data", data, 1), | ||||||
|  | 		stub.CheckArg(k.Stub, "perm", perm, 2)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) createTemp(dir, pattern string) (osFile, error) { | func (k *kstub) createTemp(dir, pattern string) (osFile, error) { | ||||||
| 	expect := k.expect("createTemp") | 	k.Helper() | ||||||
| 	return expect.ret.(osFile), expect.error( | 	expect := k.Expects("createTemp") | ||||||
| 		checkArg(k, "dir", dir, 0), | 	return expect.Ret.(osFile), expect.Error( | ||||||
| 		checkArg(k, "pattern", pattern, 1)) | 		stub.CheckArg(k.Stub, "dir", dir, 0), | ||||||
|  | 		stub.CheckArg(k.Stub, "pattern", pattern, 1)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) remove(name string) error { | func (k *kstub) remove(name string) error { | ||||||
| 	return k.expect("remove").error( | 	k.Helper() | ||||||
| 		checkArg(k, "name", name, 0)) | 	return k.Expects("remove").Error( | ||||||
|  | 		stub.CheckArg(k.Stub, "name", name, 0)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) newFile(fd uintptr, name string) *os.File { | func (k *kstub) newFile(fd uintptr, name string) *os.File { | ||||||
| 	expect := k.expect("newFile") | 	k.Helper() | ||||||
| 	if expect.error( | 	expect := k.Expects("newFile") | ||||||
| 		checkArg(k, "fd", fd, 0), | 	if expect.Error( | ||||||
| 		checkArg(k, "name", name, 1)) != nil { | 		stub.CheckArg(k.Stub, "fd", fd, 0), | ||||||
| 		k.t.FailNow() | 		stub.CheckArg(k.Stub, "name", name, 1)) != nil { | ||||||
|  | 		k.FailNow() | ||||||
| 	} | 	} | ||||||
| 	return expect.ret.(*os.File) | 	return expect.Ret.(*os.File) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) symlink(oldname, newname string) error { | func (k *kstub) symlink(oldname, newname string) error { | ||||||
| 	return k.expect("symlink").error( | 	k.Helper() | ||||||
| 		checkArg(k, "oldname", oldname, 0), | 	return k.Expects("symlink").Error( | ||||||
| 		checkArg(k, "newname", newname, 1)) | 		stub.CheckArg(k.Stub, "oldname", oldname, 0), | ||||||
|  | 		stub.CheckArg(k.Stub, "newname", newname, 1)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) readlink(name string) (string, error) { | func (k *kstub) readlink(name string) (string, error) { | ||||||
| 	expect := k.expect("readlink") | 	k.Helper() | ||||||
| 	return expect.ret.(string), expect.error( | 	expect := k.Expects("readlink") | ||||||
| 		checkArg(k, "name", name, 0)) | 	return expect.Ret.(string), expect.Error( | ||||||
|  | 		stub.CheckArg(k.Stub, "name", name, 0)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) umask(mask int) (oldmask int) { | func (k *kstub) umask(mask int) (oldmask int) { | ||||||
| 	expect := k.expect("umask") | 	k.Helper() | ||||||
| 	if !checkArg(k, "mask", mask, 0) { | 	expect := k.Expects("umask") | ||||||
| 		k.t.FailNow() | 	if !stub.CheckArg(k.Stub, "mask", mask, 0) { | ||||||
|  | 		k.FailNow() | ||||||
| 	} | 	} | ||||||
| 	return expect.ret.(int) | 	return expect.Ret.(int) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) sethostname(p []byte) (err error) { | func (k *kstub) sethostname(p []byte) (err error) { | ||||||
| 	return k.expect("sethostname").error( | 	k.Helper() | ||||||
| 		checkArgReflect(k, "p", p, 0)) | 	return k.Expects("sethostname").Error( | ||||||
|  | 		stub.CheckArgReflect(k.Stub, "p", p, 0)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) chdir(path string) (err error) { | func (k *kstub) chdir(path string) (err error) { | ||||||
| 	return k.expect("chdir").error( | 	k.Helper() | ||||||
| 		checkArg(k, "path", path, 0)) | 	return k.Expects("chdir").Error( | ||||||
|  | 		stub.CheckArg(k.Stub, "path", path, 0)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) fchdir(fd int) (err error) { | func (k *kstub) fchdir(fd int) (err error) { | ||||||
| 	return k.expect("fchdir").error( | 	k.Helper() | ||||||
| 		checkArg(k, "fd", fd, 0)) | 	return k.Expects("fchdir").Error( | ||||||
|  | 		stub.CheckArg(k.Stub, "fd", fd, 0)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) open(path string, mode int, perm uint32) (fd int, err error) { | func (k *kstub) open(path string, mode int, perm uint32) (fd int, err error) { | ||||||
| 	expect := k.expect("open") | 	k.Helper() | ||||||
| 	return expect.ret.(int), expect.error( | 	expect := k.Expects("open") | ||||||
| 		checkArg(k, "path", path, 0), | 	return expect.Ret.(int), expect.Error( | ||||||
| 		checkArg(k, "mode", mode, 1), | 		stub.CheckArg(k.Stub, "path", path, 0), | ||||||
| 		checkArg(k, "perm", perm, 2)) | 		stub.CheckArg(k.Stub, "mode", mode, 1), | ||||||
|  | 		stub.CheckArg(k.Stub, "perm", perm, 2)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) close(fd int) (err error) { | func (k *kstub) close(fd int) (err error) { | ||||||
| 	return k.expect("close").error( | 	k.Helper() | ||||||
| 		checkArg(k, "fd", fd, 0)) | 	return k.Expects("close").Error( | ||||||
|  | 		stub.CheckArg(k.Stub, "fd", fd, 0)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) pivotRoot(newroot, putold string) (err error) { | func (k *kstub) pivotRoot(newroot, putold string) (err error) { | ||||||
| 	return k.expect("pivotRoot").error( | 	k.Helper() | ||||||
| 		checkArg(k, "newroot", newroot, 0), | 	return k.Expects("pivotRoot").Error( | ||||||
| 		checkArg(k, "putold", putold, 1)) | 		stub.CheckArg(k.Stub, "newroot", newroot, 0), | ||||||
|  | 		stub.CheckArg(k.Stub, "putold", putold, 1)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) mount(source, target, fstype string, flags uintptr, data string) (err error) { | func (k *kstub) mount(source, target, fstype string, flags uintptr, data string) (err error) { | ||||||
| 	return k.expect("mount").error( | 	k.Helper() | ||||||
| 		checkArg(k, "source", source, 0), | 	return k.Expects("mount").Error( | ||||||
| 		checkArg(k, "target", target, 1), | 		stub.CheckArg(k.Stub, "source", source, 0), | ||||||
| 		checkArg(k, "fstype", fstype, 2), | 		stub.CheckArg(k.Stub, "target", target, 1), | ||||||
| 		checkArg(k, "flags", flags, 3), | 		stub.CheckArg(k.Stub, "fstype", fstype, 2), | ||||||
| 		checkArg(k, "data", data, 4)) | 		stub.CheckArg(k.Stub, "flags", flags, 3), | ||||||
|  | 		stub.CheckArg(k.Stub, "data", data, 4)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) unmount(target string, flags int) (err error) { | func (k *kstub) unmount(target string, flags int) (err error) { | ||||||
| 	return k.expect("unmount").error( | 	k.Helper() | ||||||
| 		checkArg(k, "target", target, 0), | 	return k.Expects("unmount").Error( | ||||||
| 		checkArg(k, "flags", flags, 1)) | 		stub.CheckArg(k.Stub, "target", target, 0), | ||||||
|  | 		stub.CheckArg(k.Stub, "flags", flags, 1)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) wait4(pid int, wstatus *syscall.WaitStatus, options int, rusage *syscall.Rusage) (wpid int, err error) { | func (k *kstub) wait4(pid int, wstatus *syscall.WaitStatus, options int, rusage *syscall.Rusage) (wpid int, err error) { | ||||||
| 	expect := k.expect("wait4") | 	k.Helper() | ||||||
|  | 	expect := k.Expects("wait4") | ||||||
| 	// special case to prevent leaking the wait4 goroutine when testing initEntrypoint | 	// special case to prevent leaking the wait4 goroutine when testing initEntrypoint | ||||||
| 	if v, ok := expect.args[4].(int); ok && v == 0xdeadbeef { | 	if v, ok := expect.Args[4].(int); ok && v == stub.PanicExit { | ||||||
| 		k.t.Log("terminating current goroutine as requested by kexpect") | 		k.Log("terminating current goroutine as requested by kexpect") | ||||||
| 		panic(0xdeadbeef) | 		panic(stub.PanicExit) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	wpid = expect.ret.(int) | 	wpid = expect.Ret.(int) | ||||||
| 	err = expect.error( | 	err = expect.Error( | ||||||
| 		checkArg(k, "pid", pid, 0), | 		stub.CheckArg(k.Stub, "pid", pid, 0), | ||||||
| 		checkArg(k, "options", options, 2)) | 		stub.CheckArg(k.Stub, "options", options, 2)) | ||||||
| 
 | 
 | ||||||
| 	if wstatusV, ok := expect.args[1].(syscall.WaitStatus); wstatus != nil && ok { | 	if wstatusV, ok := expect.Args[1].(syscall.WaitStatus); wstatus != nil && ok { | ||||||
| 		*wstatus = wstatusV | 		*wstatus = wstatusV | ||||||
| 	} | 	} | ||||||
| 	if rusageV, ok := expect.args[3].(syscall.Rusage); rusage != nil && ok { | 	if rusageV, ok := expect.Args[3].(syscall.Rusage); rusage != nil && ok { | ||||||
| 		*rusage = rusageV | 		*rusage = rusageV | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -696,53 +664,50 @@ func (k *kstub) wait4(pid int, wstatus *syscall.WaitStatus, options int, rusage | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) printf(format string, v ...any) { | func (k *kstub) printf(format string, v ...any) { | ||||||
| 	if k.expect("printf").error( | 	k.Helper() | ||||||
| 		checkArg(k, "format", format, 0), | 	if k.Expects("printf").Error( | ||||||
| 		checkArgReflect(k, "v", v, 1)) != nil { | 		stub.CheckArg(k.Stub, "format", format, 0), | ||||||
| 		k.t.FailNow() | 		stub.CheckArgReflect(k.Stub, "v", v, 1)) != nil { | ||||||
|  | 		k.FailNow() | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) fatal(v ...any) { | func (k *kstub) fatal(v ...any) { | ||||||
| 	if k.expect("fatal").error( | 	k.Helper() | ||||||
| 		checkArgReflect(k, "v", v, 0)) != nil { | 	if k.Expects("fatal").Error( | ||||||
| 		k.t.FailNow() | 		stub.CheckArgReflect(k.Stub, "v", v, 0)) != nil { | ||||||
|  | 		k.FailNow() | ||||||
| 	} | 	} | ||||||
| 	panic(0xdeadbeef) | 	panic(stub.PanicExit) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) fatalf(format string, v ...any) { | func (k *kstub) fatalf(format string, v ...any) { | ||||||
| 	if k.expect("fatalf").error( | 	k.Helper() | ||||||
| 		checkArg(k, "format", format, 0), | 	if k.Expects("fatalf").Error( | ||||||
| 		checkArgReflect(k, "v", v, 1)) != nil { | 		stub.CheckArg(k.Stub, "format", format, 0), | ||||||
| 		k.t.FailNow() | 		stub.CheckArgReflect(k.Stub, "v", v, 1)) != nil { | ||||||
|  | 		k.FailNow() | ||||||
| 	} | 	} | ||||||
| 	panic(0xdeadbeef) | 	panic(stub.PanicExit) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) verbose(v ...any) { | func (k *kstub) verbose(v ...any) { | ||||||
| 	if k.expect("verbose").error( | 	k.Helper() | ||||||
| 		checkArgReflect(k, "v", v, 0)) != nil { | 	if k.Expects("verbose").Error( | ||||||
| 		k.t.FailNow() | 		stub.CheckArgReflect(k.Stub, "v", v, 0)) != nil { | ||||||
|  | 		k.FailNow() | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) verbosef(format string, v ...any) { | func (k *kstub) verbosef(format string, v ...any) { | ||||||
| 	if k.expect("verbosef").error( | 	k.Helper() | ||||||
| 		checkArg(k, "format", format, 0), | 	if k.Expects("verbosef").Error( | ||||||
| 		checkArgReflect(k, "v", v, 1)) != nil { | 		stub.CheckArg(k.Stub, "format", format, 0), | ||||||
| 		k.t.FailNow() | 		stub.CheckArgReflect(k.Stub, "v", v, 1)) != nil { | ||||||
|  | 		k.FailNow() | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (k *kstub) suspend()     { k.expect("suspend") } | func (k *kstub) suspend()     { k.Helper(); k.Expects("suspend") } | ||||||
| func (k *kstub) resume() bool { return k.expect("resume").ret.(bool) } | func (k *kstub) resume() bool { k.Helper(); return k.Expects("resume").Ret.(bool) } | ||||||
| func (k *kstub) beforeExit()  { k.expect("beforeExit") } | func (k *kstub) beforeExit()  { k.Helper(); k.Expects("beforeExit") } | ||||||
| 
 |  | ||||||
| func (k *kstub) printBaseErr(err error, fallback string) { |  | ||||||
| 	if k.expect("printBaseErr").error( |  | ||||||
| 		checkArgReflect(k, "err", err, 0), |  | ||||||
| 		checkArg(k, "fallback", fallback, 1)) != nil { |  | ||||||
| 		k.t.FailNow() |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  | |||||||
| @ -8,6 +8,7 @@ import ( | |||||||
| 	"syscall" | 	"syscall" | ||||||
| 	"testing" | 	"testing" | ||||||
| 
 | 
 | ||||||
|  | 	"hakurei.app/container/stub" | ||||||
| 	"hakurei.app/container/vfs" | 	"hakurei.app/container/vfs" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| @ -30,8 +31,8 @@ func TestMessageFromError(t *testing.T) { | |||||||
| 		{"path", &os.PathError{ | 		{"path", &os.PathError{ | ||||||
| 			Op:   "mount", | 			Op:   "mount", | ||||||
| 			Path: "/sysroot", | 			Path: "/sysroot", | ||||||
| 			Err:  errUnique, | 			Err:  stub.UniqueError(0xdeadbeef), | ||||||
| 		}, "cannot mount /sysroot: unique error injected by the test suite", true}, | 		}, "cannot mount /sysroot: unique error 3735928559 injected by the test suite", true}, | ||||||
| 
 | 
 | ||||||
| 		{"absolute", &AbsoluteError{"etc/mtab"}, | 		{"absolute", &AbsoluteError{"etc/mtab"}, | ||||||
| 			`path "etc/mtab" is not absolute`, true}, | 			`path "etc/mtab" is not absolute`, true}, | ||||||
| @ -48,7 +49,7 @@ func TestMessageFromError(t *testing.T) { | |||||||
| 		{"tmpfs", TmpfsSizeError(-1), | 		{"tmpfs", TmpfsSizeError(-1), | ||||||
| 			"tmpfs size -1 out of bounds", true}, | 			"tmpfs size -1 out of bounds", true}, | ||||||
| 
 | 
 | ||||||
| 		{"unsupported", errUnique, zeroString, false}, | 		{"unsupported", stub.UniqueError(0xdeadbeef), zeroString, false}, | ||||||
| 	} | 	} | ||||||
| 	for _, tc := range testCases { | 	for _, tc := range testCases { | ||||||
| 		t.Run(tc.name, func(t *testing.T) { | 		t.Run(tc.name, func(t *testing.T) { | ||||||
| @ -144,10 +145,10 @@ func TestErrnoFallback(t *testing.T) { | |||||||
| 			Err: syscall.ETIMEDOUT, | 			Err: syscall.ETIMEDOUT, | ||||||
| 		}, syscall.ETIMEDOUT, nil}, | 		}, syscall.ETIMEDOUT, nil}, | ||||||
| 
 | 
 | ||||||
| 		{"fallback", errUnique, 0, &os.PathError{ | 		{"fallback", stub.UniqueError(0xcafebabe), 0, &os.PathError{ | ||||||
| 			Op:   "fallback", | 			Op:   "fallback", | ||||||
| 			Path: "/proc/nonexistent", | 			Path: "/proc/nonexistent", | ||||||
| 			Err:  errUnique, | 			Err:  stub.UniqueError(0xcafebabe), | ||||||
| 		}}, | 		}}, | ||||||
| 	} | 	} | ||||||
| 	for _, tc := range testCases { | 	for _, tc := range testCases { | ||||||
|  | |||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -5,6 +5,8 @@ import ( | |||||||
| 	"os" | 	"os" | ||||||
| 	"syscall" | 	"syscall" | ||||||
| 	"testing" | 	"testing" | ||||||
|  | 
 | ||||||
|  | 	"hakurei.app/container/stub" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func TestBindMountOp(t *testing.T) { | func TestBindMountOp(t *testing.T) { | ||||||
| @ -12,132 +14,132 @@ func TestBindMountOp(t *testing.T) { | |||||||
| 		{"ENOENT not optional", new(Params), &BindMountOp{ | 		{"ENOENT not optional", new(Params), &BindMountOp{ | ||||||
| 			Source: MustAbs("/bin/"), | 			Source: MustAbs("/bin/"), | ||||||
| 			Target: MustAbs("/bin/"), | 			Target: MustAbs("/bin/"), | ||||||
| 		}, []kexpect{ | 		}, []stub.Call{ | ||||||
| 			{"evalSymlinks", expectArgs{"/bin/"}, "", syscall.ENOENT}, | 			{"evalSymlinks", stub.ExpectArgs{"/bin/"}, "", syscall.ENOENT}, | ||||||
| 		}, syscall.ENOENT, nil, nil}, | 		}, syscall.ENOENT, nil, nil}, | ||||||
| 
 | 
 | ||||||
| 		{"skip optional", new(Params), &BindMountOp{ | 		{"skip optional", new(Params), &BindMountOp{ | ||||||
| 			Source: MustAbs("/bin/"), | 			Source: MustAbs("/bin/"), | ||||||
| 			Target: MustAbs("/bin/"), | 			Target: MustAbs("/bin/"), | ||||||
| 			Flags:  BindOptional, | 			Flags:  BindOptional, | ||||||
| 		}, []kexpect{ | 		}, []stub.Call{ | ||||||
| 			{"evalSymlinks", expectArgs{"/bin/"}, "", syscall.ENOENT}, | 			{"evalSymlinks", stub.ExpectArgs{"/bin/"}, "", syscall.ENOENT}, | ||||||
| 		}, nil, nil, nil}, | 		}, nil, nil, nil}, | ||||||
| 
 | 
 | ||||||
| 		{"success optional", new(Params), &BindMountOp{ | 		{"success optional", new(Params), &BindMountOp{ | ||||||
| 			Source: MustAbs("/bin/"), | 			Source: MustAbs("/bin/"), | ||||||
| 			Target: MustAbs("/bin/"), | 			Target: MustAbs("/bin/"), | ||||||
| 			Flags:  BindOptional, | 			Flags:  BindOptional, | ||||||
| 		}, []kexpect{ | 		}, []stub.Call{ | ||||||
| 			{"evalSymlinks", expectArgs{"/bin/"}, "/usr/bin", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/bin/"}, "/usr/bin", nil}, | ||||||
| 		}, nil, []kexpect{ | 		}, nil, []stub.Call{ | ||||||
| 			{"stat", expectArgs{"/host/usr/bin"}, isDirFi(true), nil}, | 			{"stat", stub.ExpectArgs{"/host/usr/bin"}, isDirFi(true), nil}, | ||||||
| 			{"mkdirAll", expectArgs{"/sysroot/bin", os.FileMode(0700)}, nil, nil}, | 			{"mkdirAll", stub.ExpectArgs{"/sysroot/bin", os.FileMode(0700)}, nil, nil}, | ||||||
| 			{"bindMount", expectArgs{"/host/usr/bin", "/sysroot/bin", uintptr(0x4005), false}, nil, nil}, | 			{"bindMount", stub.ExpectArgs{"/host/usr/bin", "/sysroot/bin", uintptr(0x4005), false}, nil, nil}, | ||||||
| 		}, nil}, | 		}, nil}, | ||||||
| 
 | 
 | ||||||
| 		{"ensureFile device", new(Params), &BindMountOp{ | 		{"ensureFile device", new(Params), &BindMountOp{ | ||||||
| 			Source: MustAbs("/dev/null"), | 			Source: MustAbs("/dev/null"), | ||||||
| 			Target: MustAbs("/dev/null"), | 			Target: MustAbs("/dev/null"), | ||||||
| 			Flags:  BindWritable | BindDevice, | 			Flags:  BindWritable | BindDevice, | ||||||
| 		}, []kexpect{ | 		}, []stub.Call{ | ||||||
| 			{"evalSymlinks", expectArgs{"/dev/null"}, "/dev/null", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/dev/null"}, "/dev/null", nil}, | ||||||
| 		}, nil, []kexpect{ | 		}, nil, []stub.Call{ | ||||||
| 			{"stat", expectArgs{"/host/dev/null"}, isDirFi(false), nil}, | 			{"stat", stub.ExpectArgs{"/host/dev/null"}, isDirFi(false), nil}, | ||||||
| 			{"ensureFile", expectArgs{"/sysroot/dev/null", os.FileMode(0444), os.FileMode(0700)}, nil, errUnique}, | 			{"ensureFile", stub.ExpectArgs{"/sysroot/dev/null", os.FileMode(0444), os.FileMode(0700)}, nil, stub.UniqueError(5)}, | ||||||
| 		}, errUnique}, | 		}, stub.UniqueError(5)}, | ||||||
| 
 | 
 | ||||||
| 		{"mkdirAll ensure", new(Params), &BindMountOp{ | 		{"mkdirAll ensure", new(Params), &BindMountOp{ | ||||||
| 			Source: MustAbs("/bin/"), | 			Source: MustAbs("/bin/"), | ||||||
| 			Target: MustAbs("/bin/"), | 			Target: MustAbs("/bin/"), | ||||||
| 			Flags:  BindEnsure, | 			Flags:  BindEnsure, | ||||||
| 		}, []kexpect{ | 		}, []stub.Call{ | ||||||
| 			{"mkdirAll", expectArgs{"/bin/", os.FileMode(0700)}, nil, errUnique}, | 			{"mkdirAll", stub.ExpectArgs{"/bin/", os.FileMode(0700)}, nil, stub.UniqueError(4)}, | ||||||
| 		}, errUnique, nil, nil}, | 		}, stub.UniqueError(4), nil, nil}, | ||||||
| 
 | 
 | ||||||
| 		{"success ensure", new(Params), &BindMountOp{ | 		{"success ensure", new(Params), &BindMountOp{ | ||||||
| 			Source: MustAbs("/bin/"), | 			Source: MustAbs("/bin/"), | ||||||
| 			Target: MustAbs("/usr/bin/"), | 			Target: MustAbs("/usr/bin/"), | ||||||
| 			Flags:  BindEnsure, | 			Flags:  BindEnsure, | ||||||
| 		}, []kexpect{ | 		}, []stub.Call{ | ||||||
| 			{"mkdirAll", expectArgs{"/bin/", os.FileMode(0700)}, nil, nil}, | 			{"mkdirAll", stub.ExpectArgs{"/bin/", os.FileMode(0700)}, nil, nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/bin/"}, "/usr/bin", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/bin/"}, "/usr/bin", nil}, | ||||||
| 		}, nil, []kexpect{ | 		}, nil, []stub.Call{ | ||||||
| 			{"stat", expectArgs{"/host/usr/bin"}, isDirFi(true), nil}, | 			{"stat", stub.ExpectArgs{"/host/usr/bin"}, isDirFi(true), nil}, | ||||||
| 			{"mkdirAll", expectArgs{"/sysroot/usr/bin", os.FileMode(0700)}, nil, nil}, | 			{"mkdirAll", stub.ExpectArgs{"/sysroot/usr/bin", os.FileMode(0700)}, nil, nil}, | ||||||
| 			{"bindMount", expectArgs{"/host/usr/bin", "/sysroot/usr/bin", uintptr(0x4005), false}, nil, nil}, | 			{"bindMount", stub.ExpectArgs{"/host/usr/bin", "/sysroot/usr/bin", uintptr(0x4005), false}, nil, nil}, | ||||||
| 		}, nil}, | 		}, nil}, | ||||||
| 
 | 
 | ||||||
| 		{"success device ro", new(Params), &BindMountOp{ | 		{"success device ro", new(Params), &BindMountOp{ | ||||||
| 			Source: MustAbs("/dev/null"), | 			Source: MustAbs("/dev/null"), | ||||||
| 			Target: MustAbs("/dev/null"), | 			Target: MustAbs("/dev/null"), | ||||||
| 			Flags:  BindDevice, | 			Flags:  BindDevice, | ||||||
| 		}, []kexpect{ | 		}, []stub.Call{ | ||||||
| 			{"evalSymlinks", expectArgs{"/dev/null"}, "/dev/null", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/dev/null"}, "/dev/null", nil}, | ||||||
| 		}, nil, []kexpect{ | 		}, nil, []stub.Call{ | ||||||
| 			{"stat", expectArgs{"/host/dev/null"}, isDirFi(false), nil}, | 			{"stat", stub.ExpectArgs{"/host/dev/null"}, isDirFi(false), nil}, | ||||||
| 			{"ensureFile", expectArgs{"/sysroot/dev/null", os.FileMode(0444), os.FileMode(0700)}, nil, nil}, | 			{"ensureFile", stub.ExpectArgs{"/sysroot/dev/null", os.FileMode(0444), os.FileMode(0700)}, nil, nil}, | ||||||
| 			{"bindMount", expectArgs{"/host/dev/null", "/sysroot/dev/null", uintptr(0x4001), false}, nil, nil}, | 			{"bindMount", stub.ExpectArgs{"/host/dev/null", "/sysroot/dev/null", uintptr(0x4001), false}, nil, nil}, | ||||||
| 		}, nil}, | 		}, nil}, | ||||||
| 
 | 
 | ||||||
| 		{"success device", new(Params), &BindMountOp{ | 		{"success device", new(Params), &BindMountOp{ | ||||||
| 			Source: MustAbs("/dev/null"), | 			Source: MustAbs("/dev/null"), | ||||||
| 			Target: MustAbs("/dev/null"), | 			Target: MustAbs("/dev/null"), | ||||||
| 			Flags:  BindWritable | BindDevice, | 			Flags:  BindWritable | BindDevice, | ||||||
| 		}, []kexpect{ | 		}, []stub.Call{ | ||||||
| 			{"evalSymlinks", expectArgs{"/dev/null"}, "/dev/null", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/dev/null"}, "/dev/null", nil}, | ||||||
| 		}, nil, []kexpect{ | 		}, nil, []stub.Call{ | ||||||
| 			{"stat", expectArgs{"/host/dev/null"}, isDirFi(false), nil}, | 			{"stat", stub.ExpectArgs{"/host/dev/null"}, isDirFi(false), nil}, | ||||||
| 			{"ensureFile", expectArgs{"/sysroot/dev/null", os.FileMode(0444), os.FileMode(0700)}, nil, nil}, | 			{"ensureFile", stub.ExpectArgs{"/sysroot/dev/null", os.FileMode(0444), os.FileMode(0700)}, nil, nil}, | ||||||
| 			{"bindMount", expectArgs{"/host/dev/null", "/sysroot/dev/null", uintptr(0x4000), false}, nil, nil}, | 			{"bindMount", stub.ExpectArgs{"/host/dev/null", "/sysroot/dev/null", uintptr(0x4000), false}, nil, nil}, | ||||||
| 		}, nil}, | 		}, nil}, | ||||||
| 
 | 
 | ||||||
| 		{"evalSymlinks", new(Params), &BindMountOp{ | 		{"evalSymlinks", new(Params), &BindMountOp{ | ||||||
| 			Source: MustAbs("/bin/"), | 			Source: MustAbs("/bin/"), | ||||||
| 			Target: MustAbs("/bin/"), | 			Target: MustAbs("/bin/"), | ||||||
| 		}, []kexpect{ | 		}, []stub.Call{ | ||||||
| 			{"evalSymlinks", expectArgs{"/bin/"}, "/usr/bin", errUnique}, | 			{"evalSymlinks", stub.ExpectArgs{"/bin/"}, "/usr/bin", stub.UniqueError(3)}, | ||||||
| 		}, errUnique, nil, nil}, | 		}, stub.UniqueError(3), nil, nil}, | ||||||
| 
 | 
 | ||||||
| 		{"stat", new(Params), &BindMountOp{ | 		{"stat", new(Params), &BindMountOp{ | ||||||
| 			Source: MustAbs("/bin/"), | 			Source: MustAbs("/bin/"), | ||||||
| 			Target: MustAbs("/bin/"), | 			Target: MustAbs("/bin/"), | ||||||
| 		}, []kexpect{ | 		}, []stub.Call{ | ||||||
| 			{"evalSymlinks", expectArgs{"/bin/"}, "/usr/bin", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/bin/"}, "/usr/bin", nil}, | ||||||
| 		}, nil, []kexpect{ | 		}, nil, []stub.Call{ | ||||||
| 			{"stat", expectArgs{"/host/usr/bin"}, isDirFi(true), errUnique}, | 			{"stat", stub.ExpectArgs{"/host/usr/bin"}, isDirFi(true), stub.UniqueError(2)}, | ||||||
| 		}, errUnique}, | 		}, stub.UniqueError(2)}, | ||||||
| 
 | 
 | ||||||
| 		{"mkdirAll", new(Params), &BindMountOp{ | 		{"mkdirAll", new(Params), &BindMountOp{ | ||||||
| 			Source: MustAbs("/bin/"), | 			Source: MustAbs("/bin/"), | ||||||
| 			Target: MustAbs("/bin/"), | 			Target: MustAbs("/bin/"), | ||||||
| 		}, []kexpect{ | 		}, []stub.Call{ | ||||||
| 			{"evalSymlinks", expectArgs{"/bin/"}, "/usr/bin", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/bin/"}, "/usr/bin", nil}, | ||||||
| 		}, nil, []kexpect{ | 		}, nil, []stub.Call{ | ||||||
| 			{"stat", expectArgs{"/host/usr/bin"}, isDirFi(true), nil}, | 			{"stat", stub.ExpectArgs{"/host/usr/bin"}, isDirFi(true), nil}, | ||||||
| 			{"mkdirAll", expectArgs{"/sysroot/bin", os.FileMode(0700)}, nil, errUnique}, | 			{"mkdirAll", stub.ExpectArgs{"/sysroot/bin", os.FileMode(0700)}, nil, stub.UniqueError(1)}, | ||||||
| 		}, errUnique}, | 		}, stub.UniqueError(1)}, | ||||||
| 
 | 
 | ||||||
| 		{"bindMount", new(Params), &BindMountOp{ | 		{"bindMount", new(Params), &BindMountOp{ | ||||||
| 			Source: MustAbs("/bin/"), | 			Source: MustAbs("/bin/"), | ||||||
| 			Target: MustAbs("/bin/"), | 			Target: MustAbs("/bin/"), | ||||||
| 		}, []kexpect{ | 		}, []stub.Call{ | ||||||
| 			{"evalSymlinks", expectArgs{"/bin/"}, "/usr/bin", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/bin/"}, "/usr/bin", nil}, | ||||||
| 		}, nil, []kexpect{ | 		}, nil, []stub.Call{ | ||||||
| 			{"stat", expectArgs{"/host/usr/bin"}, isDirFi(true), nil}, | 			{"stat", stub.ExpectArgs{"/host/usr/bin"}, isDirFi(true), nil}, | ||||||
| 			{"mkdirAll", expectArgs{"/sysroot/bin", os.FileMode(0700)}, nil, nil}, | 			{"mkdirAll", stub.ExpectArgs{"/sysroot/bin", os.FileMode(0700)}, nil, nil}, | ||||||
| 			{"bindMount", expectArgs{"/host/usr/bin", "/sysroot/bin", uintptr(0x4005), false}, nil, errUnique}, | 			{"bindMount", stub.ExpectArgs{"/host/usr/bin", "/sysroot/bin", uintptr(0x4005), false}, nil, stub.UniqueError(0)}, | ||||||
| 		}, errUnique}, | 		}, stub.UniqueError(0)}, | ||||||
| 
 | 
 | ||||||
| 		{"success", new(Params), &BindMountOp{ | 		{"success", new(Params), &BindMountOp{ | ||||||
| 			Source: MustAbs("/bin/"), | 			Source: MustAbs("/bin/"), | ||||||
| 			Target: MustAbs("/bin/"), | 			Target: MustAbs("/bin/"), | ||||||
| 		}, []kexpect{ | 		}, []stub.Call{ | ||||||
| 			{"evalSymlinks", expectArgs{"/bin/"}, "/usr/bin", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/bin/"}, "/usr/bin", nil}, | ||||||
| 		}, nil, []kexpect{ | 		}, nil, []stub.Call{ | ||||||
| 			{"stat", expectArgs{"/host/usr/bin"}, isDirFi(true), nil}, | 			{"stat", stub.ExpectArgs{"/host/usr/bin"}, isDirFi(true), nil}, | ||||||
| 			{"mkdirAll", expectArgs{"/sysroot/bin", os.FileMode(0700)}, nil, nil}, | 			{"mkdirAll", stub.ExpectArgs{"/sysroot/bin", os.FileMode(0700)}, nil, nil}, | ||||||
| 			{"bindMount", expectArgs{"/host/usr/bin", "/sysroot/bin", uintptr(0x4005), false}, nil, nil}, | 			{"bindMount", stub.ExpectArgs{"/host/usr/bin", "/sysroot/bin", uintptr(0x4005), false}, nil, nil}, | ||||||
| 		}, nil}, | 		}, nil}, | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
|  | |||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -3,6 +3,8 @@ package container | |||||||
| import ( | import ( | ||||||
| 	"os" | 	"os" | ||||||
| 	"testing" | 	"testing" | ||||||
|  | 
 | ||||||
|  | 	"hakurei.app/container/stub" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func TestMkdirOp(t *testing.T) { | func TestMkdirOp(t *testing.T) { | ||||||
| @ -10,8 +12,8 @@ func TestMkdirOp(t *testing.T) { | |||||||
| 		{"success", new(Params), &MkdirOp{ | 		{"success", new(Params), &MkdirOp{ | ||||||
| 			Path: MustAbs("/.hakurei"), | 			Path: MustAbs("/.hakurei"), | ||||||
| 			Perm: 0500, | 			Perm: 0500, | ||||||
| 		}, nil, nil, []kexpect{ | 		}, nil, nil, []stub.Call{ | ||||||
| 			{"mkdirAll", expectArgs{"/sysroot/.hakurei", os.FileMode(0500)}, nil, nil}, | 			{"mkdirAll", stub.ExpectArgs{"/sysroot/.hakurei", os.FileMode(0500)}, nil, nil}, | ||||||
| 		}, nil}, | 		}, nil}, | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -4,6 +4,8 @@ import ( | |||||||
| 	"errors" | 	"errors" | ||||||
| 	"os" | 	"os" | ||||||
| 	"testing" | 	"testing" | ||||||
|  | 
 | ||||||
|  | 	"hakurei.app/container/stub" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func TestMountOverlayOp(t *testing.T) { | func TestMountOverlayOp(t *testing.T) { | ||||||
| @ -51,13 +53,13 @@ func TestMountOverlayOp(t *testing.T) { | |||||||
| 				MustAbs("/var/lib/planterette/app/org.chromium.Chromium@debian:f92c9052"), | 				MustAbs("/var/lib/planterette/app/org.chromium.Chromium@debian:f92c9052"), | ||||||
| 			}, | 			}, | ||||||
| 			Upper: MustAbs("/"), | 			Upper: MustAbs("/"), | ||||||
| 		}, []kexpect{ | 		}, []stub.Call{ | ||||||
| 			{"evalSymlinks", expectArgs{"/var/lib/planterette/base/debian:f92c9052"}, "/var/lib/planterette/base/debian:f92c9052", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/var/lib/planterette/base/debian:f92c9052"}, "/var/lib/planterette/base/debian:f92c9052", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/var/lib/planterette/app/org.chromium.Chromium@debian:f92c9052"}, "/var/lib/planterette/app/org.chromium.Chromium@debian:f92c9052", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/var/lib/planterette/app/org.chromium.Chromium@debian:f92c9052"}, "/var/lib/planterette/app/org.chromium.Chromium@debian:f92c9052", nil}, | ||||||
| 		}, nil, []kexpect{ | 		}, nil, []stub.Call{ | ||||||
| 			{"mkdirAll", expectArgs{"/sysroot", os.FileMode(0705)}, nil, nil}, | 			{"mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0705)}, nil, nil}, | ||||||
| 			{"mkdirTemp", expectArgs{"/", "overlay.upper.*"}, "overlay.upper.32768", errUnique}, | 			{"mkdirTemp", stub.ExpectArgs{"/", "overlay.upper.*"}, "overlay.upper.32768", stub.UniqueError(6)}, | ||||||
| 		}, errUnique}, | 		}, stub.UniqueError(6)}, | ||||||
| 
 | 
 | ||||||
| 		{"mkdirTemp work ephemeral", &Params{ParentPerm: 0705}, &MountOverlayOp{ | 		{"mkdirTemp work ephemeral", &Params{ParentPerm: 0705}, &MountOverlayOp{ | ||||||
| 			Target: MustAbs("/"), | 			Target: MustAbs("/"), | ||||||
| @ -66,14 +68,14 @@ func TestMountOverlayOp(t *testing.T) { | |||||||
| 				MustAbs("/var/lib/planterette/app/org.chromium.Chromium@debian:f92c9052"), | 				MustAbs("/var/lib/planterette/app/org.chromium.Chromium@debian:f92c9052"), | ||||||
| 			}, | 			}, | ||||||
| 			Upper: MustAbs("/"), | 			Upper: MustAbs("/"), | ||||||
| 		}, []kexpect{ | 		}, []stub.Call{ | ||||||
| 			{"evalSymlinks", expectArgs{"/var/lib/planterette/base/debian:f92c9052"}, "/var/lib/planterette/base/debian:f92c9052", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/var/lib/planterette/base/debian:f92c9052"}, "/var/lib/planterette/base/debian:f92c9052", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/var/lib/planterette/app/org.chromium.Chromium@debian:f92c9052"}, "/var/lib/planterette/app/org.chromium.Chromium@debian:f92c9052", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/var/lib/planterette/app/org.chromium.Chromium@debian:f92c9052"}, "/var/lib/planterette/app/org.chromium.Chromium@debian:f92c9052", nil}, | ||||||
| 		}, nil, []kexpect{ | 		}, nil, []stub.Call{ | ||||||
| 			{"mkdirAll", expectArgs{"/sysroot", os.FileMode(0705)}, nil, nil}, | 			{"mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0705)}, nil, nil}, | ||||||
| 			{"mkdirTemp", expectArgs{"/", "overlay.upper.*"}, "overlay.upper.32768", nil}, | 			{"mkdirTemp", stub.ExpectArgs{"/", "overlay.upper.*"}, "overlay.upper.32768", nil}, | ||||||
| 			{"mkdirTemp", expectArgs{"/", "overlay.work.*"}, "overlay.work.32768", errUnique}, | 			{"mkdirTemp", stub.ExpectArgs{"/", "overlay.work.*"}, "overlay.work.32768", stub.UniqueError(5)}, | ||||||
| 		}, errUnique}, | 		}, stub.UniqueError(5)}, | ||||||
| 
 | 
 | ||||||
| 		{"success ephemeral", &Params{ParentPerm: 0705}, &MountOverlayOp{ | 		{"success ephemeral", &Params{ParentPerm: 0705}, &MountOverlayOp{ | ||||||
| 			Target: MustAbs("/"), | 			Target: MustAbs("/"), | ||||||
| @ -82,14 +84,14 @@ func TestMountOverlayOp(t *testing.T) { | |||||||
| 				MustAbs("/var/lib/planterette/app/org.chromium.Chromium@debian:f92c9052"), | 				MustAbs("/var/lib/planterette/app/org.chromium.Chromium@debian:f92c9052"), | ||||||
| 			}, | 			}, | ||||||
| 			Upper: MustAbs("/"), | 			Upper: MustAbs("/"), | ||||||
| 		}, []kexpect{ | 		}, []stub.Call{ | ||||||
| 			{"evalSymlinks", expectArgs{"/var/lib/planterette/base/debian:f92c9052"}, "/var/lib/planterette/base/debian:f92c9052", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/var/lib/planterette/base/debian:f92c9052"}, "/var/lib/planterette/base/debian:f92c9052", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/var/lib/planterette/app/org.chromium.Chromium@debian:f92c9052"}, "/var/lib/planterette/app/org.chromium.Chromium@debian:f92c9052", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/var/lib/planterette/app/org.chromium.Chromium@debian:f92c9052"}, "/var/lib/planterette/app/org.chromium.Chromium@debian:f92c9052", nil}, | ||||||
| 		}, nil, []kexpect{ | 		}, nil, []stub.Call{ | ||||||
| 			{"mkdirAll", expectArgs{"/sysroot", os.FileMode(0705)}, nil, nil}, | 			{"mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0705)}, nil, nil}, | ||||||
| 			{"mkdirTemp", expectArgs{"/", "overlay.upper.*"}, "overlay.upper.32768", nil}, | 			{"mkdirTemp", stub.ExpectArgs{"/", "overlay.upper.*"}, "overlay.upper.32768", nil}, | ||||||
| 			{"mkdirTemp", expectArgs{"/", "overlay.work.*"}, "overlay.work.32768", nil}, | 			{"mkdirTemp", stub.ExpectArgs{"/", "overlay.work.*"}, "overlay.work.32768", nil}, | ||||||
| 			{"mount", expectArgs{"overlay", "/sysroot", "overlay", uintptr(0), "" + | 			{"mount", stub.ExpectArgs{"overlay", "/sysroot", "overlay", uintptr(0), "" + | ||||||
| 				"upperdir=overlay.upper.32768," + | 				"upperdir=overlay.upper.32768," + | ||||||
| 				"workdir=overlay.work.32768," + | 				"workdir=overlay.work.32768," + | ||||||
| 				"lowerdir=" + | 				"lowerdir=" + | ||||||
| @ -103,10 +105,10 @@ func TestMountOverlayOp(t *testing.T) { | |||||||
| 			Lower: []*Absolute{ | 			Lower: []*Absolute{ | ||||||
| 				MustAbs("/mnt-root/nix/.ro-store"), | 				MustAbs("/mnt-root/nix/.ro-store"), | ||||||
| 			}, | 			}, | ||||||
| 		}, []kexpect{ | 		}, []stub.Call{ | ||||||
| 			{"evalSymlinks", expectArgs{"/mnt-root/nix/.ro-store"}, "/mnt-root/nix/.ro-store", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.ro-store"}, "/mnt-root/nix/.ro-store", nil}, | ||||||
| 		}, nil, []kexpect{ | 		}, nil, []stub.Call{ | ||||||
| 			{"mkdirAll", expectArgs{"/sysroot/nix/store", os.FileMode(0755)}, nil, nil}, | 			{"mkdirAll", stub.ExpectArgs{"/sysroot/nix/store", os.FileMode(0755)}, nil, nil}, | ||||||
| 		}, &OverlayArgumentError{OverlayReadonlyLower, zeroString}}, | 		}, &OverlayArgumentError{OverlayReadonlyLower, zeroString}}, | ||||||
| 
 | 
 | ||||||
| 		{"success ro noPrefix", &Params{ParentPerm: 0755}, &MountOverlayOp{ | 		{"success ro noPrefix", &Params{ParentPerm: 0755}, &MountOverlayOp{ | ||||||
| @ -116,12 +118,12 @@ func TestMountOverlayOp(t *testing.T) { | |||||||
| 				MustAbs("/mnt-root/nix/.ro-store0"), | 				MustAbs("/mnt-root/nix/.ro-store0"), | ||||||
| 			}, | 			}, | ||||||
| 			noPrefix: true, | 			noPrefix: true, | ||||||
| 		}, []kexpect{ | 		}, []stub.Call{ | ||||||
| 			{"evalSymlinks", expectArgs{"/mnt-root/nix/.ro-store"}, "/mnt-root/nix/.ro-store", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.ro-store"}, "/mnt-root/nix/.ro-store", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/mnt-root/nix/.ro-store0"}, "/mnt-root/nix/.ro-store0", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.ro-store0"}, "/mnt-root/nix/.ro-store0", nil}, | ||||||
| 		}, nil, []kexpect{ | 		}, nil, []stub.Call{ | ||||||
| 			{"mkdirAll", expectArgs{"/nix/store", os.FileMode(0755)}, nil, nil}, | 			{"mkdirAll", stub.ExpectArgs{"/nix/store", os.FileMode(0755)}, nil, nil}, | ||||||
| 			{"mount", expectArgs{"overlay", "/nix/store", "overlay", uintptr(0), "" + | 			{"mount", stub.ExpectArgs{"overlay", "/nix/store", "overlay", uintptr(0), "" + | ||||||
| 				"lowerdir=" + | 				"lowerdir=" + | ||||||
| 				"/host/mnt-root/nix/.ro-store:" + | 				"/host/mnt-root/nix/.ro-store:" + | ||||||
| 				"/host/mnt-root/nix/.ro-store0," + | 				"/host/mnt-root/nix/.ro-store0," + | ||||||
| @ -134,12 +136,12 @@ func TestMountOverlayOp(t *testing.T) { | |||||||
| 				MustAbs("/mnt-root/nix/.ro-store"), | 				MustAbs("/mnt-root/nix/.ro-store"), | ||||||
| 				MustAbs("/mnt-root/nix/.ro-store0"), | 				MustAbs("/mnt-root/nix/.ro-store0"), | ||||||
| 			}, | 			}, | ||||||
| 		}, []kexpect{ | 		}, []stub.Call{ | ||||||
| 			{"evalSymlinks", expectArgs{"/mnt-root/nix/.ro-store"}, "/mnt-root/nix/.ro-store", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.ro-store"}, "/mnt-root/nix/.ro-store", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/mnt-root/nix/.ro-store0"}, "/mnt-root/nix/.ro-store0", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.ro-store0"}, "/mnt-root/nix/.ro-store0", nil}, | ||||||
| 		}, nil, []kexpect{ | 		}, nil, []stub.Call{ | ||||||
| 			{"mkdirAll", expectArgs{"/sysroot/nix/store", os.FileMode(0755)}, nil, nil}, | 			{"mkdirAll", stub.ExpectArgs{"/sysroot/nix/store", os.FileMode(0755)}, nil, nil}, | ||||||
| 			{"mount", expectArgs{"overlay", "/sysroot/nix/store", "overlay", uintptr(0), "" + | 			{"mount", stub.ExpectArgs{"overlay", "/sysroot/nix/store", "overlay", uintptr(0), "" + | ||||||
| 				"lowerdir=" + | 				"lowerdir=" + | ||||||
| 				"/host/mnt-root/nix/.ro-store:" + | 				"/host/mnt-root/nix/.ro-store:" + | ||||||
| 				"/host/mnt-root/nix/.ro-store0," + | 				"/host/mnt-root/nix/.ro-store0," + | ||||||
| @ -150,11 +152,11 @@ func TestMountOverlayOp(t *testing.T) { | |||||||
| 			Target: MustAbs("/nix/store"), | 			Target: MustAbs("/nix/store"), | ||||||
| 			Upper:  MustAbs("/mnt-root/nix/.rw-store/upper"), | 			Upper:  MustAbs("/mnt-root/nix/.rw-store/upper"), | ||||||
| 			Work:   MustAbs("/mnt-root/nix/.rw-store/work"), | 			Work:   MustAbs("/mnt-root/nix/.rw-store/work"), | ||||||
| 		}, []kexpect{ | 		}, []stub.Call{ | ||||||
| 			{"evalSymlinks", expectArgs{"/mnt-root/nix/.rw-store/upper"}, "/mnt-root/nix/.rw-store/.upper", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/upper"}, "/mnt-root/nix/.rw-store/.upper", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/mnt-root/nix/.rw-store/work"}, "/mnt-root/nix/.rw-store/.work", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/work"}, "/mnt-root/nix/.rw-store/.work", nil}, | ||||||
| 		}, nil, []kexpect{ | 		}, nil, []stub.Call{ | ||||||
| 			{"mkdirAll", expectArgs{"/sysroot/nix/store", os.FileMode(0700)}, nil, nil}, | 			{"mkdirAll", stub.ExpectArgs{"/sysroot/nix/store", os.FileMode(0700)}, nil, nil}, | ||||||
| 		}, &OverlayArgumentError{OverlayEmptyLower, zeroString}}, | 		}, &OverlayArgumentError{OverlayEmptyLower, zeroString}}, | ||||||
| 
 | 
 | ||||||
| 		{"evalSymlinks upper", &Params{ParentPerm: 0700}, &MountOverlayOp{ | 		{"evalSymlinks upper", &Params{ParentPerm: 0700}, &MountOverlayOp{ | ||||||
| @ -162,70 +164,70 @@ func TestMountOverlayOp(t *testing.T) { | |||||||
| 			Lower:  []*Absolute{MustAbs("/mnt-root/nix/.ro-store")}, | 			Lower:  []*Absolute{MustAbs("/mnt-root/nix/.ro-store")}, | ||||||
| 			Upper:  MustAbs("/mnt-root/nix/.rw-store/upper"), | 			Upper:  MustAbs("/mnt-root/nix/.rw-store/upper"), | ||||||
| 			Work:   MustAbs("/mnt-root/nix/.rw-store/work"), | 			Work:   MustAbs("/mnt-root/nix/.rw-store/work"), | ||||||
| 		}, []kexpect{ | 		}, []stub.Call{ | ||||||
| 			{"evalSymlinks", expectArgs{"/mnt-root/nix/.rw-store/upper"}, "/mnt-root/nix/.rw-store/.upper", errUnique}, | 			{"evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/upper"}, "/mnt-root/nix/.rw-store/.upper", stub.UniqueError(4)}, | ||||||
| 		}, errUnique, nil, nil}, | 		}, stub.UniqueError(4), nil, nil}, | ||||||
| 
 | 
 | ||||||
| 		{"evalSymlinks work", &Params{ParentPerm: 0700}, &MountOverlayOp{ | 		{"evalSymlinks work", &Params{ParentPerm: 0700}, &MountOverlayOp{ | ||||||
| 			Target: MustAbs("/nix/store"), | 			Target: MustAbs("/nix/store"), | ||||||
| 			Lower:  []*Absolute{MustAbs("/mnt-root/nix/.ro-store")}, | 			Lower:  []*Absolute{MustAbs("/mnt-root/nix/.ro-store")}, | ||||||
| 			Upper:  MustAbs("/mnt-root/nix/.rw-store/upper"), | 			Upper:  MustAbs("/mnt-root/nix/.rw-store/upper"), | ||||||
| 			Work:   MustAbs("/mnt-root/nix/.rw-store/work"), | 			Work:   MustAbs("/mnt-root/nix/.rw-store/work"), | ||||||
| 		}, []kexpect{ | 		}, []stub.Call{ | ||||||
| 			{"evalSymlinks", expectArgs{"/mnt-root/nix/.rw-store/upper"}, "/mnt-root/nix/.rw-store/.upper", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/upper"}, "/mnt-root/nix/.rw-store/.upper", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/mnt-root/nix/.rw-store/work"}, "/mnt-root/nix/.rw-store/.work", errUnique}, | 			{"evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/work"}, "/mnt-root/nix/.rw-store/.work", stub.UniqueError(3)}, | ||||||
| 		}, errUnique, nil, nil}, | 		}, stub.UniqueError(3), nil, nil}, | ||||||
| 
 | 
 | ||||||
| 		{"evalSymlinks lower", &Params{ParentPerm: 0700}, &MountOverlayOp{ | 		{"evalSymlinks lower", &Params{ParentPerm: 0700}, &MountOverlayOp{ | ||||||
| 			Target: MustAbs("/nix/store"), | 			Target: MustAbs("/nix/store"), | ||||||
| 			Lower:  []*Absolute{MustAbs("/mnt-root/nix/.ro-store")}, | 			Lower:  []*Absolute{MustAbs("/mnt-root/nix/.ro-store")}, | ||||||
| 			Upper:  MustAbs("/mnt-root/nix/.rw-store/upper"), | 			Upper:  MustAbs("/mnt-root/nix/.rw-store/upper"), | ||||||
| 			Work:   MustAbs("/mnt-root/nix/.rw-store/work"), | 			Work:   MustAbs("/mnt-root/nix/.rw-store/work"), | ||||||
| 		}, []kexpect{ | 		}, []stub.Call{ | ||||||
| 			{"evalSymlinks", expectArgs{"/mnt-root/nix/.rw-store/upper"}, "/mnt-root/nix/.rw-store/.upper", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/upper"}, "/mnt-root/nix/.rw-store/.upper", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/mnt-root/nix/.rw-store/work"}, "/mnt-root/nix/.rw-store/.work", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/work"}, "/mnt-root/nix/.rw-store/.work", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/mnt-root/nix/.ro-store"}, "/mnt-root/nix/ro-store", errUnique}, | 			{"evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.ro-store"}, "/mnt-root/nix/ro-store", stub.UniqueError(2)}, | ||||||
| 		}, errUnique, nil, nil}, | 		}, stub.UniqueError(2), nil, nil}, | ||||||
| 
 | 
 | ||||||
| 		{"mkdirAll", &Params{ParentPerm: 0700}, &MountOverlayOp{ | 		{"mkdirAll", &Params{ParentPerm: 0700}, &MountOverlayOp{ | ||||||
| 			Target: MustAbs("/nix/store"), | 			Target: MustAbs("/nix/store"), | ||||||
| 			Lower:  []*Absolute{MustAbs("/mnt-root/nix/.ro-store")}, | 			Lower:  []*Absolute{MustAbs("/mnt-root/nix/.ro-store")}, | ||||||
| 			Upper:  MustAbs("/mnt-root/nix/.rw-store/upper"), | 			Upper:  MustAbs("/mnt-root/nix/.rw-store/upper"), | ||||||
| 			Work:   MustAbs("/mnt-root/nix/.rw-store/work"), | 			Work:   MustAbs("/mnt-root/nix/.rw-store/work"), | ||||||
| 		}, []kexpect{ | 		}, []stub.Call{ | ||||||
| 			{"evalSymlinks", expectArgs{"/mnt-root/nix/.rw-store/upper"}, "/mnt-root/nix/.rw-store/.upper", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/upper"}, "/mnt-root/nix/.rw-store/.upper", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/mnt-root/nix/.rw-store/work"}, "/mnt-root/nix/.rw-store/.work", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/work"}, "/mnt-root/nix/.rw-store/.work", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/mnt-root/nix/.ro-store"}, "/mnt-root/nix/ro-store", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.ro-store"}, "/mnt-root/nix/ro-store", nil}, | ||||||
| 		}, nil, []kexpect{ | 		}, nil, []stub.Call{ | ||||||
| 			{"mkdirAll", expectArgs{"/sysroot/nix/store", os.FileMode(0700)}, nil, errUnique}, | 			{"mkdirAll", stub.ExpectArgs{"/sysroot/nix/store", os.FileMode(0700)}, nil, stub.UniqueError(1)}, | ||||||
| 		}, errUnique}, | 		}, stub.UniqueError(1)}, | ||||||
| 
 | 
 | ||||||
| 		{"mount", &Params{ParentPerm: 0700}, &MountOverlayOp{ | 		{"mount", &Params{ParentPerm: 0700}, &MountOverlayOp{ | ||||||
| 			Target: MustAbs("/nix/store"), | 			Target: MustAbs("/nix/store"), | ||||||
| 			Lower:  []*Absolute{MustAbs("/mnt-root/nix/.ro-store")}, | 			Lower:  []*Absolute{MustAbs("/mnt-root/nix/.ro-store")}, | ||||||
| 			Upper:  MustAbs("/mnt-root/nix/.rw-store/upper"), | 			Upper:  MustAbs("/mnt-root/nix/.rw-store/upper"), | ||||||
| 			Work:   MustAbs("/mnt-root/nix/.rw-store/work"), | 			Work:   MustAbs("/mnt-root/nix/.rw-store/work"), | ||||||
| 		}, []kexpect{ | 		}, []stub.Call{ | ||||||
| 			{"evalSymlinks", expectArgs{"/mnt-root/nix/.rw-store/upper"}, "/mnt-root/nix/.rw-store/.upper", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/upper"}, "/mnt-root/nix/.rw-store/.upper", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/mnt-root/nix/.rw-store/work"}, "/mnt-root/nix/.rw-store/.work", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/work"}, "/mnt-root/nix/.rw-store/.work", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/mnt-root/nix/.ro-store"}, "/mnt-root/nix/ro-store", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.ro-store"}, "/mnt-root/nix/ro-store", nil}, | ||||||
| 		}, nil, []kexpect{ | 		}, nil, []stub.Call{ | ||||||
| 			{"mkdirAll", expectArgs{"/sysroot/nix/store", os.FileMode(0700)}, nil, nil}, | 			{"mkdirAll", stub.ExpectArgs{"/sysroot/nix/store", os.FileMode(0700)}, nil, nil}, | ||||||
| 			{"mount", expectArgs{"overlay", "/sysroot/nix/store", "overlay", uintptr(0), "upperdir=/host/mnt-root/nix/.rw-store/.upper,workdir=/host/mnt-root/nix/.rw-store/.work,lowerdir=/host/mnt-root/nix/ro-store,userxattr"}, nil, errUnique}, | 			{"mount", stub.ExpectArgs{"overlay", "/sysroot/nix/store", "overlay", uintptr(0), "upperdir=/host/mnt-root/nix/.rw-store/.upper,workdir=/host/mnt-root/nix/.rw-store/.work,lowerdir=/host/mnt-root/nix/ro-store,userxattr"}, nil, stub.UniqueError(0)}, | ||||||
| 		}, errUnique}, | 		}, stub.UniqueError(0)}, | ||||||
| 
 | 
 | ||||||
| 		{"success single layer", &Params{ParentPerm: 0700}, &MountOverlayOp{ | 		{"success single layer", &Params{ParentPerm: 0700}, &MountOverlayOp{ | ||||||
| 			Target: MustAbs("/nix/store"), | 			Target: MustAbs("/nix/store"), | ||||||
| 			Lower:  []*Absolute{MustAbs("/mnt-root/nix/.ro-store")}, | 			Lower:  []*Absolute{MustAbs("/mnt-root/nix/.ro-store")}, | ||||||
| 			Upper:  MustAbs("/mnt-root/nix/.rw-store/upper"), | 			Upper:  MustAbs("/mnt-root/nix/.rw-store/upper"), | ||||||
| 			Work:   MustAbs("/mnt-root/nix/.rw-store/work"), | 			Work:   MustAbs("/mnt-root/nix/.rw-store/work"), | ||||||
| 		}, []kexpect{ | 		}, []stub.Call{ | ||||||
| 			{"evalSymlinks", expectArgs{"/mnt-root/nix/.rw-store/upper"}, "/mnt-root/nix/.rw-store/.upper", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/upper"}, "/mnt-root/nix/.rw-store/.upper", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/mnt-root/nix/.rw-store/work"}, "/mnt-root/nix/.rw-store/.work", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/work"}, "/mnt-root/nix/.rw-store/.work", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/mnt-root/nix/.ro-store"}, "/mnt-root/nix/ro-store", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.ro-store"}, "/mnt-root/nix/ro-store", nil}, | ||||||
| 		}, nil, []kexpect{ | 		}, nil, []stub.Call{ | ||||||
| 			{"mkdirAll", expectArgs{"/sysroot/nix/store", os.FileMode(0700)}, nil, nil}, | 			{"mkdirAll", stub.ExpectArgs{"/sysroot/nix/store", os.FileMode(0700)}, nil, nil}, | ||||||
| 			{"mount", expectArgs{"overlay", "/sysroot/nix/store", "overlay", uintptr(0), "" + | 			{"mount", stub.ExpectArgs{"overlay", "/sysroot/nix/store", "overlay", uintptr(0), "" + | ||||||
| 				"upperdir=/host/mnt-root/nix/.rw-store/.upper," + | 				"upperdir=/host/mnt-root/nix/.rw-store/.upper," + | ||||||
| 				"workdir=/host/mnt-root/nix/.rw-store/.work," + | 				"workdir=/host/mnt-root/nix/.rw-store/.work," + | ||||||
| 				"lowerdir=/host/mnt-root/nix/ro-store," + | 				"lowerdir=/host/mnt-root/nix/ro-store," + | ||||||
| @ -243,17 +245,17 @@ func TestMountOverlayOp(t *testing.T) { | |||||||
| 			}, | 			}, | ||||||
| 			Upper: MustAbs("/mnt-root/nix/.rw-store/upper"), | 			Upper: MustAbs("/mnt-root/nix/.rw-store/upper"), | ||||||
| 			Work:  MustAbs("/mnt-root/nix/.rw-store/work"), | 			Work:  MustAbs("/mnt-root/nix/.rw-store/work"), | ||||||
| 		}, []kexpect{ | 		}, []stub.Call{ | ||||||
| 			{"evalSymlinks", expectArgs{"/mnt-root/nix/.rw-store/upper"}, "/mnt-root/nix/.rw-store/.upper", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/upper"}, "/mnt-root/nix/.rw-store/.upper", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/mnt-root/nix/.rw-store/work"}, "/mnt-root/nix/.rw-store/.work", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/work"}, "/mnt-root/nix/.rw-store/.work", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/mnt-root/nix/.ro-store"}, "/mnt-root/nix/ro-store", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.ro-store"}, "/mnt-root/nix/ro-store", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/mnt-root/nix/.ro-store0"}, "/mnt-root/nix/ro-store0", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.ro-store0"}, "/mnt-root/nix/ro-store0", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/mnt-root/nix/.ro-store1"}, "/mnt-root/nix/ro-store1", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.ro-store1"}, "/mnt-root/nix/ro-store1", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/mnt-root/nix/.ro-store2"}, "/mnt-root/nix/ro-store2", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.ro-store2"}, "/mnt-root/nix/ro-store2", nil}, | ||||||
| 			{"evalSymlinks", expectArgs{"/mnt-root/nix/.ro-store3"}, "/mnt-root/nix/ro-store3", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.ro-store3"}, "/mnt-root/nix/ro-store3", nil}, | ||||||
| 		}, nil, []kexpect{ | 		}, nil, []stub.Call{ | ||||||
| 			{"mkdirAll", expectArgs{"/sysroot/nix/store", os.FileMode(0700)}, nil, nil}, | 			{"mkdirAll", stub.ExpectArgs{"/sysroot/nix/store", os.FileMode(0700)}, nil, nil}, | ||||||
| 			{"mount", expectArgs{"overlay", "/sysroot/nix/store", "overlay", uintptr(0), "" + | 			{"mount", stub.ExpectArgs{"overlay", "/sysroot/nix/store", "overlay", uintptr(0), "" + | ||||||
| 				"upperdir=/host/mnt-root/nix/.rw-store/.upper," + | 				"upperdir=/host/mnt-root/nix/.rw-store/.upper," + | ||||||
| 				"workdir=/host/mnt-root/nix/.rw-store/.work," + | 				"workdir=/host/mnt-root/nix/.rw-store/.work," + | ||||||
| 				"lowerdir=" + | 				"lowerdir=" + | ||||||
|  | |||||||
| @ -3,6 +3,8 @@ package container | |||||||
| import ( | import ( | ||||||
| 	"os" | 	"os" | ||||||
| 	"testing" | 	"testing" | ||||||
|  | 
 | ||||||
|  | 	"hakurei.app/container/stub" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func TestTmpfileOp(t *testing.T) { | func TestTmpfileOp(t *testing.T) { | ||||||
| @ -16,59 +18,59 @@ func TestTmpfileOp(t *testing.T) { | |||||||
| 		{"createTemp", &Params{ParentPerm: 0700}, &TmpfileOp{ | 		{"createTemp", &Params{ParentPerm: 0700}, &TmpfileOp{ | ||||||
| 			Path: samplePath, | 			Path: samplePath, | ||||||
| 			Data: sampleData, | 			Data: sampleData, | ||||||
| 		}, nil, nil, []kexpect{ | 		}, nil, nil, []stub.Call{ | ||||||
| 			{"createTemp", expectArgs{"/", "tmp.*"}, newCheckedFile(t, "tmp.32768", sampleDataString, nil), errUnique}, | 			{"createTemp", stub.ExpectArgs{"/", "tmp.*"}, newCheckedFile(t, "tmp.32768", sampleDataString, nil), stub.UniqueError(5)}, | ||||||
| 		}, errUnique}, | 		}, stub.UniqueError(5)}, | ||||||
| 
 | 
 | ||||||
| 		{"Write", &Params{ParentPerm: 0700}, &TmpfileOp{ | 		{"Write", &Params{ParentPerm: 0700}, &TmpfileOp{ | ||||||
| 			Path: samplePath, | 			Path: samplePath, | ||||||
| 			Data: sampleData, | 			Data: sampleData, | ||||||
| 		}, nil, nil, []kexpect{ | 		}, nil, nil, []stub.Call{ | ||||||
| 			{"createTemp", expectArgs{"/", "tmp.*"}, writeErrOsFile{errUnique}, nil}, | 			{"createTemp", stub.ExpectArgs{"/", "tmp.*"}, writeErrOsFile{stub.UniqueError(4)}, nil}, | ||||||
| 		}, errUnique}, | 		}, stub.UniqueError(4)}, | ||||||
| 
 | 
 | ||||||
| 		{"Close", &Params{ParentPerm: 0700}, &TmpfileOp{ | 		{"Close", &Params{ParentPerm: 0700}, &TmpfileOp{ | ||||||
| 			Path: samplePath, | 			Path: samplePath, | ||||||
| 			Data: sampleData, | 			Data: sampleData, | ||||||
| 		}, nil, nil, []kexpect{ | 		}, nil, nil, []stub.Call{ | ||||||
| 			{"createTemp", expectArgs{"/", "tmp.*"}, newCheckedFile(t, "tmp.32768", sampleDataString, errUnique), nil}, | 			{"createTemp", stub.ExpectArgs{"/", "tmp.*"}, newCheckedFile(t, "tmp.32768", sampleDataString, stub.UniqueError(3)), nil}, | ||||||
| 		}, errUnique}, | 		}, stub.UniqueError(3)}, | ||||||
| 
 | 
 | ||||||
| 		{"ensureFile", &Params{ParentPerm: 0700}, &TmpfileOp{ | 		{"ensureFile", &Params{ParentPerm: 0700}, &TmpfileOp{ | ||||||
| 			Path: samplePath, | 			Path: samplePath, | ||||||
| 			Data: sampleData, | 			Data: sampleData, | ||||||
| 		}, nil, nil, []kexpect{ | 		}, nil, nil, []stub.Call{ | ||||||
| 			{"createTemp", expectArgs{"/", "tmp.*"}, newCheckedFile(t, "tmp.32768", sampleDataString, nil), nil}, | 			{"createTemp", stub.ExpectArgs{"/", "tmp.*"}, newCheckedFile(t, "tmp.32768", sampleDataString, nil), nil}, | ||||||
| 			{"ensureFile", expectArgs{"/sysroot/etc/passwd", os.FileMode(0444), os.FileMode(0700)}, nil, errUnique}, | 			{"ensureFile", stub.ExpectArgs{"/sysroot/etc/passwd", os.FileMode(0444), os.FileMode(0700)}, nil, stub.UniqueError(2)}, | ||||||
| 		}, errUnique}, | 		}, stub.UniqueError(2)}, | ||||||
| 
 | 
 | ||||||
| 		{"bindMount", &Params{ParentPerm: 0700}, &TmpfileOp{ | 		{"bindMount", &Params{ParentPerm: 0700}, &TmpfileOp{ | ||||||
| 			Path: samplePath, | 			Path: samplePath, | ||||||
| 			Data: sampleData, | 			Data: sampleData, | ||||||
| 		}, nil, nil, []kexpect{ | 		}, nil, nil, []stub.Call{ | ||||||
| 			{"createTemp", expectArgs{"/", "tmp.*"}, newCheckedFile(t, "tmp.32768", sampleDataString, nil), nil}, | 			{"createTemp", stub.ExpectArgs{"/", "tmp.*"}, newCheckedFile(t, "tmp.32768", sampleDataString, nil), nil}, | ||||||
| 			{"ensureFile", expectArgs{"/sysroot/etc/passwd", os.FileMode(0444), os.FileMode(0700)}, nil, nil}, | 			{"ensureFile", stub.ExpectArgs{"/sysroot/etc/passwd", os.FileMode(0444), os.FileMode(0700)}, nil, nil}, | ||||||
| 			{"bindMount", expectArgs{"tmp.32768", "/sysroot/etc/passwd", uintptr(0x5), false}, nil, errUnique}, | 			{"bindMount", stub.ExpectArgs{"tmp.32768", "/sysroot/etc/passwd", uintptr(0x5), false}, nil, stub.UniqueError(1)}, | ||||||
| 		}, errUnique}, | 		}, stub.UniqueError(1)}, | ||||||
| 
 | 
 | ||||||
| 		{"remove", &Params{ParentPerm: 0700}, &TmpfileOp{ | 		{"remove", &Params{ParentPerm: 0700}, &TmpfileOp{ | ||||||
| 			Path: samplePath, | 			Path: samplePath, | ||||||
| 			Data: sampleData, | 			Data: sampleData, | ||||||
| 		}, nil, nil, []kexpect{ | 		}, nil, nil, []stub.Call{ | ||||||
| 			{"createTemp", expectArgs{"/", "tmp.*"}, newCheckedFile(t, "tmp.32768", sampleDataString, nil), nil}, | 			{"createTemp", stub.ExpectArgs{"/", "tmp.*"}, newCheckedFile(t, "tmp.32768", sampleDataString, nil), nil}, | ||||||
| 			{"ensureFile", expectArgs{"/sysroot/etc/passwd", os.FileMode(0444), os.FileMode(0700)}, nil, nil}, | 			{"ensureFile", stub.ExpectArgs{"/sysroot/etc/passwd", os.FileMode(0444), os.FileMode(0700)}, nil, nil}, | ||||||
| 			{"bindMount", expectArgs{"tmp.32768", "/sysroot/etc/passwd", uintptr(0x5), false}, nil, nil}, | 			{"bindMount", stub.ExpectArgs{"tmp.32768", "/sysroot/etc/passwd", uintptr(0x5), false}, nil, nil}, | ||||||
| 			{"remove", expectArgs{"tmp.32768"}, nil, errUnique}, | 			{"remove", stub.ExpectArgs{"tmp.32768"}, nil, stub.UniqueError(0)}, | ||||||
| 		}, errUnique}, | 		}, stub.UniqueError(0)}, | ||||||
| 
 | 
 | ||||||
| 		{"success", &Params{ParentPerm: 0700}, &TmpfileOp{ | 		{"success", &Params{ParentPerm: 0700}, &TmpfileOp{ | ||||||
| 			Path: samplePath, | 			Path: samplePath, | ||||||
| 			Data: sampleData, | 			Data: sampleData, | ||||||
| 		}, nil, nil, []kexpect{ | 		}, nil, nil, []stub.Call{ | ||||||
| 			{"createTemp", expectArgs{"/", "tmp.*"}, newCheckedFile(t, "tmp.32768", sampleDataString, nil), nil}, | 			{"createTemp", stub.ExpectArgs{"/", "tmp.*"}, newCheckedFile(t, "tmp.32768", sampleDataString, nil), nil}, | ||||||
| 			{"ensureFile", expectArgs{"/sysroot/etc/passwd", os.FileMode(0444), os.FileMode(0700)}, nil, nil}, | 			{"ensureFile", stub.ExpectArgs{"/sysroot/etc/passwd", os.FileMode(0444), os.FileMode(0700)}, nil, nil}, | ||||||
| 			{"bindMount", expectArgs{"tmp.32768", "/sysroot/etc/passwd", uintptr(0x5), false}, nil, nil}, | 			{"bindMount", stub.ExpectArgs{"tmp.32768", "/sysroot/etc/passwd", uintptr(0x5), false}, nil, nil}, | ||||||
| 			{"remove", expectArgs{"tmp.32768"}, nil, nil}, | 			{"remove", stub.ExpectArgs{"tmp.32768"}, nil, nil}, | ||||||
| 		}, nil}, | 		}, nil}, | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -3,6 +3,8 @@ package container | |||||||
| import ( | import ( | ||||||
| 	"os" | 	"os" | ||||||
| 	"testing" | 	"testing" | ||||||
|  | 
 | ||||||
|  | 	"hakurei.app/container/stub" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func TestMountProcOp(t *testing.T) { | func TestMountProcOp(t *testing.T) { | ||||||
| @ -10,16 +12,16 @@ func TestMountProcOp(t *testing.T) { | |||||||
| 		{"mkdir", &Params{ParentPerm: 0755}, | 		{"mkdir", &Params{ParentPerm: 0755}, | ||||||
| 			&MountProcOp{ | 			&MountProcOp{ | ||||||
| 				Target: MustAbs("/proc/"), | 				Target: MustAbs("/proc/"), | ||||||
| 			}, nil, nil, []kexpect{ | 			}, nil, nil, []stub.Call{ | ||||||
| 				{"mkdirAll", expectArgs{"/sysroot/proc", os.FileMode(0755)}, nil, errUnique}, | 				{"mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0755)}, nil, stub.UniqueError(0)}, | ||||||
| 			}, errUnique}, | 			}, stub.UniqueError(0)}, | ||||||
| 
 | 
 | ||||||
| 		{"success", &Params{ParentPerm: 0700}, | 		{"success", &Params{ParentPerm: 0700}, | ||||||
| 			&MountProcOp{ | 			&MountProcOp{ | ||||||
| 				Target: MustAbs("/proc/"), | 				Target: MustAbs("/proc/"), | ||||||
| 			}, nil, nil, []kexpect{ | 			}, nil, nil, []stub.Call{ | ||||||
| 				{"mkdirAll", expectArgs{"/sysroot/proc", os.FileMode(0700)}, nil, nil}, | 				{"mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0700)}, nil, nil}, | ||||||
| 				{"mount", expectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil}, | 				{"mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil}, | ||||||
| 			}, nil}, | 			}, nil}, | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -3,6 +3,8 @@ package container | |||||||
| import ( | import ( | ||||||
| 	"syscall" | 	"syscall" | ||||||
| 	"testing" | 	"testing" | ||||||
|  | 
 | ||||||
|  | 	"hakurei.app/container/stub" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func TestRemountOp(t *testing.T) { | func TestRemountOp(t *testing.T) { | ||||||
| @ -10,8 +12,8 @@ func TestRemountOp(t *testing.T) { | |||||||
| 		{"success", new(Params), &RemountOp{ | 		{"success", new(Params), &RemountOp{ | ||||||
| 			Target: MustAbs("/"), | 			Target: MustAbs("/"), | ||||||
| 			Flags:  syscall.MS_RDONLY, | 			Flags:  syscall.MS_RDONLY, | ||||||
| 		}, nil, nil, []kexpect{ | 		}, nil, nil, []stub.Call{ | ||||||
| 			{"remount", expectArgs{"/sysroot", uintptr(1)}, nil, nil}, | 			{"remount", stub.ExpectArgs{"/sysroot", uintptr(1)}, nil, nil}, | ||||||
| 		}, nil}, | 		}, nil}, | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -3,6 +3,8 @@ package container | |||||||
| import ( | import ( | ||||||
| 	"os" | 	"os" | ||||||
| 	"testing" | 	"testing" | ||||||
|  | 
 | ||||||
|  | 	"hakurei.app/container/stub" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func TestSymlinkOp(t *testing.T) { | func TestSymlinkOp(t *testing.T) { | ||||||
| @ -10,9 +12,9 @@ func TestSymlinkOp(t *testing.T) { | |||||||
| 		{"mkdir", &Params{ParentPerm: 0700}, &SymlinkOp{ | 		{"mkdir", &Params{ParentPerm: 0700}, &SymlinkOp{ | ||||||
| 			Target:   MustAbs("/etc/nixos"), | 			Target:   MustAbs("/etc/nixos"), | ||||||
| 			LinkName: "/etc/static/nixos", | 			LinkName: "/etc/static/nixos", | ||||||
| 		}, nil, nil, []kexpect{ | 		}, nil, nil, []stub.Call{ | ||||||
| 			{"mkdirAll", expectArgs{"/sysroot/etc", os.FileMode(0700)}, nil, errUnique}, | 			{"mkdirAll", stub.ExpectArgs{"/sysroot/etc", os.FileMode(0700)}, nil, stub.UniqueError(1)}, | ||||||
| 		}, errUnique}, | 		}, stub.UniqueError(1)}, | ||||||
| 
 | 
 | ||||||
| 		{"abs", &Params{ParentPerm: 0755}, &SymlinkOp{ | 		{"abs", &Params{ParentPerm: 0755}, &SymlinkOp{ | ||||||
| 			Target:      MustAbs("/etc/mtab"), | 			Target:      MustAbs("/etc/mtab"), | ||||||
| @ -24,27 +26,27 @@ func TestSymlinkOp(t *testing.T) { | |||||||
| 			Target:      MustAbs("/etc/mtab"), | 			Target:      MustAbs("/etc/mtab"), | ||||||
| 			LinkName:    "/etc/mtab", | 			LinkName:    "/etc/mtab", | ||||||
| 			Dereference: true, | 			Dereference: true, | ||||||
| 		}, []kexpect{ | 		}, []stub.Call{ | ||||||
| 			{"readlink", expectArgs{"/etc/mtab"}, "/proc/mounts", errUnique}, | 			{"readlink", stub.ExpectArgs{"/etc/mtab"}, "/proc/mounts", stub.UniqueError(0)}, | ||||||
| 		}, errUnique, nil, nil}, | 		}, stub.UniqueError(0), nil, nil}, | ||||||
| 
 | 
 | ||||||
| 		{"success noderef", &Params{ParentPerm: 0700}, &SymlinkOp{ | 		{"success noderef", &Params{ParentPerm: 0700}, &SymlinkOp{ | ||||||
| 			Target:   MustAbs("/etc/nixos"), | 			Target:   MustAbs("/etc/nixos"), | ||||||
| 			LinkName: "/etc/static/nixos", | 			LinkName: "/etc/static/nixos", | ||||||
| 		}, nil, nil, []kexpect{ | 		}, nil, nil, []stub.Call{ | ||||||
| 			{"mkdirAll", expectArgs{"/sysroot/etc", os.FileMode(0700)}, nil, nil}, | 			{"mkdirAll", stub.ExpectArgs{"/sysroot/etc", os.FileMode(0700)}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{"/etc/static/nixos", "/sysroot/etc/nixos"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{"/etc/static/nixos", "/sysroot/etc/nixos"}, nil, nil}, | ||||||
| 		}, nil}, | 		}, nil}, | ||||||
| 
 | 
 | ||||||
| 		{"success", &Params{ParentPerm: 0755}, &SymlinkOp{ | 		{"success", &Params{ParentPerm: 0755}, &SymlinkOp{ | ||||||
| 			Target:      MustAbs("/etc/mtab"), | 			Target:      MustAbs("/etc/mtab"), | ||||||
| 			LinkName:    "/etc/mtab", | 			LinkName:    "/etc/mtab", | ||||||
| 			Dereference: true, | 			Dereference: true, | ||||||
| 		}, []kexpect{ | 		}, []stub.Call{ | ||||||
| 			{"readlink", expectArgs{"/etc/mtab"}, "/proc/mounts", nil}, | 			{"readlink", stub.ExpectArgs{"/etc/mtab"}, "/proc/mounts", nil}, | ||||||
| 		}, nil, []kexpect{ | 		}, nil, []stub.Call{ | ||||||
| 			{"mkdirAll", expectArgs{"/sysroot/etc", os.FileMode(0755)}, nil, nil}, | 			{"mkdirAll", stub.ExpectArgs{"/sysroot/etc", os.FileMode(0755)}, nil, nil}, | ||||||
| 			{"symlink", expectArgs{"/proc/mounts", "/sysroot/etc/mtab"}, nil, nil}, | 			{"symlink", stub.ExpectArgs{"/proc/mounts", "/sysroot/etc/mtab"}, nil, nil}, | ||||||
| 		}, nil}, | 		}, nil}, | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -4,6 +4,8 @@ import ( | |||||||
| 	"os" | 	"os" | ||||||
| 	"syscall" | 	"syscall" | ||||||
| 	"testing" | 	"testing" | ||||||
|  | 
 | ||||||
|  | 	"hakurei.app/container/stub" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func TestMountTmpfsOp(t *testing.T) { | func TestMountTmpfsOp(t *testing.T) { | ||||||
| @ -25,8 +27,8 @@ func TestMountTmpfsOp(t *testing.T) { | |||||||
| 			Path:   MustAbs("/run/user/1000/"), | 			Path:   MustAbs("/run/user/1000/"), | ||||||
| 			Size:   1 << 10, | 			Size:   1 << 10, | ||||||
| 			Perm:   0700, | 			Perm:   0700, | ||||||
| 		}, nil, nil, []kexpect{ | 		}, nil, nil, []stub.Call{ | ||||||
| 			{"mountTmpfs", expectArgs{ | 			{"mountTmpfs", stub.ExpectArgs{ | ||||||
| 				"ephemeral",              // fsname | 				"ephemeral",              // fsname | ||||||
| 				"/sysroot/run/user/1000", // target | 				"/sysroot/run/user/1000", // target | ||||||
| 				uintptr(0),               // flags | 				uintptr(0),               // flags | ||||||
|  | |||||||
| @ -5,6 +5,7 @@ import ( | |||||||
| 	"syscall" | 	"syscall" | ||||||
| 	"testing" | 	"testing" | ||||||
| 
 | 
 | ||||||
|  | 	"hakurei.app/container/stub" | ||||||
| 	"hakurei.app/container/vfs" | 	"hakurei.app/container/vfs" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| @ -12,25 +13,25 @@ func TestBindMount(t *testing.T) { | |||||||
| 	checkSimple(t, "bindMount", []simpleTestCase{ | 	checkSimple(t, "bindMount", []simpleTestCase{ | ||||||
| 		{"mount", func(k syscallDispatcher) error { | 		{"mount", func(k syscallDispatcher) error { | ||||||
| 			return newProcPaths(k, hostPath).bindMount("/host/nix", "/sysroot/nix", syscall.MS_RDONLY, true) | 			return newProcPaths(k, hostPath).bindMount("/host/nix", "/sysroot/nix", syscall.MS_RDONLY, true) | ||||||
| 		}, [][]kexpect{{ | 		}, stub.Expect{Calls: []stub.Call{ | ||||||
| 			{"verbosef", expectArgs{"resolved %q flags %#x", []any{"/sysroot/nix", uintptr(1)}}, nil, nil}, | 			{"verbosef", stub.ExpectArgs{"resolved %q flags %#x", []any{"/sysroot/nix", uintptr(1)}}, nil, nil}, | ||||||
| 			{"mount", expectArgs{"/host/nix", "/sysroot/nix", "", uintptr(0x9000), ""}, nil, errUnique}, | 			{"mount", stub.ExpectArgs{"/host/nix", "/sysroot/nix", "", uintptr(0x9000), ""}, nil, stub.UniqueError(0xbad)}, | ||||||
| 		}}, errUnique}, | 		}}, stub.UniqueError(0xbad)}, | ||||||
| 
 | 
 | ||||||
| 		{"success ne", func(k syscallDispatcher) error { | 		{"success ne", func(k syscallDispatcher) error { | ||||||
| 			return newProcPaths(k, hostPath).bindMount("/host/nix", "/sysroot/.host-nix", syscall.MS_RDONLY, false) | 			return newProcPaths(k, hostPath).bindMount("/host/nix", "/sysroot/.host-nix", syscall.MS_RDONLY, false) | ||||||
| 		}, [][]kexpect{{ | 		}, stub.Expect{Calls: []stub.Call{ | ||||||
| 			{"verbosef", expectArgs{"resolved %q on %q flags %#x", []any{"/host/nix", "/sysroot/.host-nix", uintptr(1)}}, nil, nil}, | 			{"verbosef", stub.ExpectArgs{"resolved %q on %q flags %#x", []any{"/host/nix", "/sysroot/.host-nix", uintptr(1)}}, nil, nil}, | ||||||
| 			{"mount", expectArgs{"/host/nix", "/sysroot/.host-nix", "", uintptr(0x9000), ""}, nil, nil}, | 			{"mount", stub.ExpectArgs{"/host/nix", "/sysroot/.host-nix", "", uintptr(0x9000), ""}, nil, nil}, | ||||||
| 			{"remount", expectArgs{"/sysroot/.host-nix", uintptr(1)}, nil, nil}, | 			{"remount", stub.ExpectArgs{"/sysroot/.host-nix", uintptr(1)}, nil, nil}, | ||||||
| 		}}, nil}, | 		}}, nil}, | ||||||
| 
 | 
 | ||||||
| 		{"success", func(k syscallDispatcher) error { | 		{"success", func(k syscallDispatcher) error { | ||||||
| 			return newProcPaths(k, hostPath).bindMount("/host/nix", "/sysroot/nix", syscall.MS_RDONLY, true) | 			return newProcPaths(k, hostPath).bindMount("/host/nix", "/sysroot/nix", syscall.MS_RDONLY, true) | ||||||
| 		}, [][]kexpect{{ | 		}, stub.Expect{Calls: []stub.Call{ | ||||||
| 			{"verbosef", expectArgs{"resolved %q flags %#x", []any{"/sysroot/nix", uintptr(1)}}, nil, nil}, | 			{"verbosef", stub.ExpectArgs{"resolved %q flags %#x", []any{"/sysroot/nix", uintptr(1)}}, nil, nil}, | ||||||
| 			{"mount", expectArgs{"/host/nix", "/sysroot/nix", "", uintptr(0x9000), ""}, nil, nil}, | 			{"mount", stub.ExpectArgs{"/host/nix", "/sysroot/nix", "", uintptr(0x9000), ""}, nil, nil}, | ||||||
| 			{"remount", expectArgs{"/sysroot/nix", uintptr(1)}, nil, nil}, | 			{"remount", stub.ExpectArgs{"/sysroot/nix", uintptr(1)}, nil, nil}, | ||||||
| 		}}, nil}, | 		}}, nil}, | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
| @ -81,138 +82,138 @@ func TestRemount(t *testing.T) { | |||||||
| 	checkSimple(t, "remount", []simpleTestCase{ | 	checkSimple(t, "remount", []simpleTestCase{ | ||||||
| 		{"evalSymlinks", func(k syscallDispatcher) error { | 		{"evalSymlinks", func(k syscallDispatcher) error { | ||||||
| 			return newProcPaths(k, hostPath).remount("/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV) | 			return newProcPaths(k, hostPath).remount("/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV) | ||||||
| 		}, [][]kexpect{{ | 		}, stub.Expect{Calls: []stub.Call{ | ||||||
| 			{"evalSymlinks", expectArgs{"/sysroot/nix"}, "/sysroot/nix", errUnique}, | 			{"evalSymlinks", stub.ExpectArgs{"/sysroot/nix"}, "/sysroot/nix", stub.UniqueError(6)}, | ||||||
| 		}}, errUnique}, | 		}}, stub.UniqueError(6)}, | ||||||
| 
 | 
 | ||||||
| 		{"open", func(k syscallDispatcher) error { | 		{"open", func(k syscallDispatcher) error { | ||||||
| 			return newProcPaths(k, hostPath).remount("/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV) | 			return newProcPaths(k, hostPath).remount("/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV) | ||||||
| 		}, [][]kexpect{{ | 		}, stub.Expect{Calls: []stub.Call{ | ||||||
| 			{"evalSymlinks", expectArgs{"/sysroot/nix"}, "/sysroot/nix", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/sysroot/nix"}, "/sysroot/nix", nil}, | ||||||
| 			{"open", expectArgs{"/sysroot/nix", 0x280000, uint32(0)}, 0xdeadbeef, errUnique}, | 			{"open", stub.ExpectArgs{"/sysroot/nix", 0x280000, uint32(0)}, 0xdeadbeef, stub.UniqueError(5)}, | ||||||
| 		}}, &os.PathError{Op: "open", Path: "/sysroot/nix", Err: errUnique}}, | 		}}, &os.PathError{Op: "open", Path: "/sysroot/nix", Err: stub.UniqueError(5)}}, | ||||||
| 
 | 
 | ||||||
| 		{"readlink", func(k syscallDispatcher) error { | 		{"readlink", func(k syscallDispatcher) error { | ||||||
| 			return newProcPaths(k, hostPath).remount("/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV) | 			return newProcPaths(k, hostPath).remount("/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV) | ||||||
| 		}, [][]kexpect{{ | 		}, stub.Expect{Calls: []stub.Call{ | ||||||
| 			{"evalSymlinks", expectArgs{"/sysroot/nix"}, "/sysroot/nix", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/sysroot/nix"}, "/sysroot/nix", nil}, | ||||||
| 			{"open", expectArgs{"/sysroot/nix", 0x280000, uint32(0)}, 0xdeadbeef, nil}, | 			{"open", stub.ExpectArgs{"/sysroot/nix", 0x280000, uint32(0)}, 0xdeadbeef, nil}, | ||||||
| 			{"readlink", expectArgs{"/host/proc/self/fd/3735928559"}, "/sysroot/nix", errUnique}, | 			{"readlink", stub.ExpectArgs{"/host/proc/self/fd/3735928559"}, "/sysroot/nix", stub.UniqueError(4)}, | ||||||
| 		}}, errUnique}, | 		}}, stub.UniqueError(4)}, | ||||||
| 
 | 
 | ||||||
| 		{"close", func(k syscallDispatcher) error { | 		{"close", func(k syscallDispatcher) error { | ||||||
| 			return newProcPaths(k, hostPath).remount("/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV) | 			return newProcPaths(k, hostPath).remount("/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV) | ||||||
| 		}, [][]kexpect{{ | 		}, stub.Expect{Calls: []stub.Call{ | ||||||
| 			{"evalSymlinks", expectArgs{"/sysroot/nix"}, "/sysroot/nix", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/sysroot/nix"}, "/sysroot/nix", nil}, | ||||||
| 			{"open", expectArgs{"/sysroot/nix", 0x280000, uint32(0)}, 0xdeadbeef, nil}, | 			{"open", stub.ExpectArgs{"/sysroot/nix", 0x280000, uint32(0)}, 0xdeadbeef, nil}, | ||||||
| 			{"readlink", expectArgs{"/host/proc/self/fd/3735928559"}, "/sysroot/nix", nil}, | 			{"readlink", stub.ExpectArgs{"/host/proc/self/fd/3735928559"}, "/sysroot/nix", nil}, | ||||||
| 			{"close", expectArgs{0xdeadbeef}, nil, errUnique}, | 			{"close", stub.ExpectArgs{0xdeadbeef}, nil, stub.UniqueError(3)}, | ||||||
| 		}}, &os.PathError{Op: "close", Path: "/sysroot/nix", Err: errUnique}}, | 		}}, &os.PathError{Op: "close", Path: "/sysroot/nix", Err: stub.UniqueError(3)}}, | ||||||
| 
 | 
 | ||||||
| 		{"mountinfo no match", func(k syscallDispatcher) error { | 		{"mountinfo no match", func(k syscallDispatcher) error { | ||||||
| 			return newProcPaths(k, hostPath).remount("/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV) | 			return newProcPaths(k, hostPath).remount("/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV) | ||||||
| 		}, [][]kexpect{{ | 		}, stub.Expect{Calls: []stub.Call{ | ||||||
| 			{"evalSymlinks", expectArgs{"/sysroot/nix"}, "/sysroot/.hakurei", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/sysroot/nix"}, "/sysroot/.hakurei", nil}, | ||||||
| 			{"verbosef", expectArgs{"target resolves to %q", []any{"/sysroot/.hakurei"}}, nil, nil}, | 			{"verbosef", stub.ExpectArgs{"target resolves to %q", []any{"/sysroot/.hakurei"}}, nil, nil}, | ||||||
| 			{"open", expectArgs{"/sysroot/.hakurei", 0x280000, uint32(0)}, 0xdeadbeef, nil}, | 			{"open", stub.ExpectArgs{"/sysroot/.hakurei", 0x280000, uint32(0)}, 0xdeadbeef, nil}, | ||||||
| 			{"readlink", expectArgs{"/host/proc/self/fd/3735928559"}, "/sysroot/.hakurei", nil}, | 			{"readlink", stub.ExpectArgs{"/host/proc/self/fd/3735928559"}, "/sysroot/.hakurei", nil}, | ||||||
| 			{"close", expectArgs{0xdeadbeef}, nil, nil}, | 			{"close", stub.ExpectArgs{0xdeadbeef}, nil, nil}, | ||||||
| 			{"openNew", expectArgs{"/host/proc/self/mountinfo"}, newConstFile(sampleMountinfoNix), nil}, | 			{"openNew", stub.ExpectArgs{"/host/proc/self/mountinfo"}, newConstFile(sampleMountinfoNix), nil}, | ||||||
| 		}}, &vfs.DecoderError{Op: "unfold", Line: -1, Err: vfs.UnfoldTargetError("/sysroot/.hakurei")}}, | 		}}, &vfs.DecoderError{Op: "unfold", Line: -1, Err: vfs.UnfoldTargetError("/sysroot/.hakurei")}}, | ||||||
| 
 | 
 | ||||||
| 		{"mountinfo", func(k syscallDispatcher) error { | 		{"mountinfo", func(k syscallDispatcher) error { | ||||||
| 			return newProcPaths(k, hostPath).remount("/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV) | 			return newProcPaths(k, hostPath).remount("/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV) | ||||||
| 		}, [][]kexpect{{ | 		}, stub.Expect{Calls: []stub.Call{ | ||||||
| 			{"evalSymlinks", expectArgs{"/sysroot/nix"}, "/sysroot/nix", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/sysroot/nix"}, "/sysroot/nix", nil}, | ||||||
| 			{"open", expectArgs{"/sysroot/nix", 0x280000, uint32(0)}, 0xdeadbeef, nil}, | 			{"open", stub.ExpectArgs{"/sysroot/nix", 0x280000, uint32(0)}, 0xdeadbeef, nil}, | ||||||
| 			{"readlink", expectArgs{"/host/proc/self/fd/3735928559"}, "/sysroot/nix", nil}, | 			{"readlink", stub.ExpectArgs{"/host/proc/self/fd/3735928559"}, "/sysroot/nix", nil}, | ||||||
| 			{"close", expectArgs{0xdeadbeef}, nil, nil}, | 			{"close", stub.ExpectArgs{0xdeadbeef}, nil, nil}, | ||||||
| 			{"openNew", expectArgs{"/host/proc/self/mountinfo"}, newConstFile("\x00"), nil}, | 			{"openNew", stub.ExpectArgs{"/host/proc/self/mountinfo"}, newConstFile("\x00"), nil}, | ||||||
| 		}}, &vfs.DecoderError{Op: "parse", Line: 0, Err: vfs.ErrMountInfoFields}}, | 		}}, &vfs.DecoderError{Op: "parse", Line: 0, Err: vfs.ErrMountInfoFields}}, | ||||||
| 
 | 
 | ||||||
| 		{"mount", func(k syscallDispatcher) error { | 		{"mount", func(k syscallDispatcher) error { | ||||||
| 			return newProcPaths(k, hostPath).remount("/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV) | 			return newProcPaths(k, hostPath).remount("/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV) | ||||||
| 		}, [][]kexpect{{ | 		}, stub.Expect{Calls: []stub.Call{ | ||||||
| 			{"evalSymlinks", expectArgs{"/sysroot/nix"}, "/sysroot/nix", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/sysroot/nix"}, "/sysroot/nix", nil}, | ||||||
| 			{"open", expectArgs{"/sysroot/nix", 0x280000, uint32(0)}, 0xdeadbeef, nil}, | 			{"open", stub.ExpectArgs{"/sysroot/nix", 0x280000, uint32(0)}, 0xdeadbeef, nil}, | ||||||
| 			{"readlink", expectArgs{"/host/proc/self/fd/3735928559"}, "/sysroot/nix", nil}, | 			{"readlink", stub.ExpectArgs{"/host/proc/self/fd/3735928559"}, "/sysroot/nix", nil}, | ||||||
| 			{"close", expectArgs{0xdeadbeef}, nil, nil}, | 			{"close", stub.ExpectArgs{0xdeadbeef}, nil, nil}, | ||||||
| 			{"openNew", expectArgs{"/host/proc/self/mountinfo"}, newConstFile(sampleMountinfoNix), nil}, | 			{"openNew", stub.ExpectArgs{"/host/proc/self/mountinfo"}, newConstFile(sampleMountinfoNix), nil}, | ||||||
| 			{"mount", expectArgs{"none", "/sysroot/nix", "", uintptr(0x209027), ""}, nil, errUnique}, | 			{"mount", stub.ExpectArgs{"none", "/sysroot/nix", "", uintptr(0x209027), ""}, nil, stub.UniqueError(2)}, | ||||||
| 		}}, errUnique}, | 		}}, stub.UniqueError(2)}, | ||||||
| 
 | 
 | ||||||
| 		{"mount propagate", func(k syscallDispatcher) error { | 		{"mount propagate", func(k syscallDispatcher) error { | ||||||
| 			return newProcPaths(k, hostPath).remount("/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV) | 			return newProcPaths(k, hostPath).remount("/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV) | ||||||
| 		}, [][]kexpect{{ | 		}, stub.Expect{Calls: []stub.Call{ | ||||||
| 			{"evalSymlinks", expectArgs{"/sysroot/nix"}, "/sysroot/nix", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/sysroot/nix"}, "/sysroot/nix", nil}, | ||||||
| 			{"open", expectArgs{"/sysroot/nix", 0x280000, uint32(0)}, 0xdeadbeef, nil}, | 			{"open", stub.ExpectArgs{"/sysroot/nix", 0x280000, uint32(0)}, 0xdeadbeef, nil}, | ||||||
| 			{"readlink", expectArgs{"/host/proc/self/fd/3735928559"}, "/sysroot/nix", nil}, | 			{"readlink", stub.ExpectArgs{"/host/proc/self/fd/3735928559"}, "/sysroot/nix", nil}, | ||||||
| 			{"close", expectArgs{0xdeadbeef}, nil, nil}, | 			{"close", stub.ExpectArgs{0xdeadbeef}, nil, nil}, | ||||||
| 			{"openNew", expectArgs{"/host/proc/self/mountinfo"}, newConstFile(sampleMountinfoNix), nil}, | 			{"openNew", stub.ExpectArgs{"/host/proc/self/mountinfo"}, newConstFile(sampleMountinfoNix), nil}, | ||||||
| 			{"mount", expectArgs{"none", "/sysroot/nix", "", uintptr(0x209027), ""}, nil, nil}, | 			{"mount", stub.ExpectArgs{"none", "/sysroot/nix", "", uintptr(0x209027), ""}, nil, nil}, | ||||||
| 			{"mount", expectArgs{"none", "/sysroot/nix/.ro-store", "", uintptr(0x209027), ""}, nil, errUnique}, | 			{"mount", stub.ExpectArgs{"none", "/sysroot/nix/.ro-store", "", uintptr(0x209027), ""}, nil, stub.UniqueError(1)}, | ||||||
| 		}}, errUnique}, | 		}}, stub.UniqueError(1)}, | ||||||
| 
 | 
 | ||||||
| 		{"success toplevel", func(k syscallDispatcher) error { | 		{"success toplevel", func(k syscallDispatcher) error { | ||||||
| 			return newProcPaths(k, hostPath).remount("/sysroot/bin", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV) | 			return newProcPaths(k, hostPath).remount("/sysroot/bin", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV) | ||||||
| 		}, [][]kexpect{{ | 		}, stub.Expect{Calls: []stub.Call{ | ||||||
| 			{"evalSymlinks", expectArgs{"/sysroot/bin"}, "/sysroot/bin", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/sysroot/bin"}, "/sysroot/bin", nil}, | ||||||
| 			{"open", expectArgs{"/sysroot/bin", 0x280000, uint32(0)}, 0xbabe, nil}, | 			{"open", stub.ExpectArgs{"/sysroot/bin", 0x280000, uint32(0)}, 0xbabe, nil}, | ||||||
| 			{"readlink", expectArgs{"/host/proc/self/fd/47806"}, "/sysroot/bin", nil}, | 			{"readlink", stub.ExpectArgs{"/host/proc/self/fd/47806"}, "/sysroot/bin", nil}, | ||||||
| 			{"close", expectArgs{0xbabe}, nil, nil}, | 			{"close", stub.ExpectArgs{0xbabe}, nil, nil}, | ||||||
| 			{"openNew", expectArgs{"/host/proc/self/mountinfo"}, newConstFile(sampleMountinfoNix), nil}, | 			{"openNew", stub.ExpectArgs{"/host/proc/self/mountinfo"}, newConstFile(sampleMountinfoNix), nil}, | ||||||
| 			{"mount", expectArgs{"none", "/sysroot/bin", "", uintptr(0x209027), ""}, nil, nil}, | 			{"mount", stub.ExpectArgs{"none", "/sysroot/bin", "", uintptr(0x209027), ""}, nil, nil}, | ||||||
| 		}}, nil}, | 		}}, nil}, | ||||||
| 
 | 
 | ||||||
| 		{"success EACCES", func(k syscallDispatcher) error { | 		{"success EACCES", func(k syscallDispatcher) error { | ||||||
| 			return newProcPaths(k, hostPath).remount("/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV) | 			return newProcPaths(k, hostPath).remount("/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV) | ||||||
| 		}, [][]kexpect{{ | 		}, stub.Expect{Calls: []stub.Call{ | ||||||
| 			{"evalSymlinks", expectArgs{"/sysroot/nix"}, "/sysroot/nix", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/sysroot/nix"}, "/sysroot/nix", nil}, | ||||||
| 			{"open", expectArgs{"/sysroot/nix", 0x280000, uint32(0)}, 0xdeadbeef, nil}, | 			{"open", stub.ExpectArgs{"/sysroot/nix", 0x280000, uint32(0)}, 0xdeadbeef, nil}, | ||||||
| 			{"readlink", expectArgs{"/host/proc/self/fd/3735928559"}, "/sysroot/nix", nil}, | 			{"readlink", stub.ExpectArgs{"/host/proc/self/fd/3735928559"}, "/sysroot/nix", nil}, | ||||||
| 			{"close", expectArgs{0xdeadbeef}, nil, nil}, | 			{"close", stub.ExpectArgs{0xdeadbeef}, nil, nil}, | ||||||
| 			{"openNew", expectArgs{"/host/proc/self/mountinfo"}, newConstFile(sampleMountinfoNix), nil}, | 			{"openNew", stub.ExpectArgs{"/host/proc/self/mountinfo"}, newConstFile(sampleMountinfoNix), nil}, | ||||||
| 			{"mount", expectArgs{"none", "/sysroot/nix", "", uintptr(0x209027), ""}, nil, nil}, | 			{"mount", stub.ExpectArgs{"none", "/sysroot/nix", "", uintptr(0x209027), ""}, nil, nil}, | ||||||
| 			{"mount", expectArgs{"none", "/sysroot/nix/.ro-store", "", uintptr(0x209027), ""}, nil, syscall.EACCES}, | 			{"mount", stub.ExpectArgs{"none", "/sysroot/nix/.ro-store", "", uintptr(0x209027), ""}, nil, syscall.EACCES}, | ||||||
| 			{"mount", expectArgs{"none", "/sysroot/nix/store", "", uintptr(0x209027), ""}, nil, nil}, | 			{"mount", stub.ExpectArgs{"none", "/sysroot/nix/store", "", uintptr(0x209027), ""}, nil, nil}, | ||||||
| 		}}, nil}, | 		}}, nil}, | ||||||
| 
 | 
 | ||||||
| 		{"success no propagate", func(k syscallDispatcher) error { | 		{"success no propagate", func(k syscallDispatcher) error { | ||||||
| 			return newProcPaths(k, hostPath).remount("/sysroot/nix", syscall.MS_RDONLY|syscall.MS_NODEV) | 			return newProcPaths(k, hostPath).remount("/sysroot/nix", syscall.MS_RDONLY|syscall.MS_NODEV) | ||||||
| 		}, [][]kexpect{{ | 		}, stub.Expect{Calls: []stub.Call{ | ||||||
| 			{"evalSymlinks", expectArgs{"/sysroot/nix"}, "/sysroot/nix", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/sysroot/nix"}, "/sysroot/nix", nil}, | ||||||
| 			{"open", expectArgs{"/sysroot/nix", 0x280000, uint32(0)}, 0xdeadbeef, nil}, | 			{"open", stub.ExpectArgs{"/sysroot/nix", 0x280000, uint32(0)}, 0xdeadbeef, nil}, | ||||||
| 			{"readlink", expectArgs{"/host/proc/self/fd/3735928559"}, "/sysroot/nix", nil}, | 			{"readlink", stub.ExpectArgs{"/host/proc/self/fd/3735928559"}, "/sysroot/nix", nil}, | ||||||
| 			{"close", expectArgs{0xdeadbeef}, nil, nil}, | 			{"close", stub.ExpectArgs{0xdeadbeef}, nil, nil}, | ||||||
| 			{"openNew", expectArgs{"/host/proc/self/mountinfo"}, newConstFile(sampleMountinfoNix), nil}, | 			{"openNew", stub.ExpectArgs{"/host/proc/self/mountinfo"}, newConstFile(sampleMountinfoNix), nil}, | ||||||
| 			{"mount", expectArgs{"none", "/sysroot/nix", "", uintptr(0x209027), ""}, nil, nil}, | 			{"mount", stub.ExpectArgs{"none", "/sysroot/nix", "", uintptr(0x209027), ""}, nil, nil}, | ||||||
| 		}}, nil}, | 		}}, nil}, | ||||||
| 
 | 
 | ||||||
| 		{"success case sensitive", func(k syscallDispatcher) error { | 		{"success case sensitive", func(k syscallDispatcher) error { | ||||||
| 			return newProcPaths(k, hostPath).remount("/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV) | 			return newProcPaths(k, hostPath).remount("/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV) | ||||||
| 		}, [][]kexpect{{ | 		}, stub.Expect{Calls: []stub.Call{ | ||||||
| 			{"evalSymlinks", expectArgs{"/sysroot/nix"}, "/sysroot/nix", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/sysroot/nix"}, "/sysroot/nix", nil}, | ||||||
| 			{"open", expectArgs{"/sysroot/nix", 0x280000, uint32(0)}, 0xdeadbeef, nil}, | 			{"open", stub.ExpectArgs{"/sysroot/nix", 0x280000, uint32(0)}, 0xdeadbeef, nil}, | ||||||
| 			{"readlink", expectArgs{"/host/proc/self/fd/3735928559"}, "/sysroot/nix", nil}, | 			{"readlink", stub.ExpectArgs{"/host/proc/self/fd/3735928559"}, "/sysroot/nix", nil}, | ||||||
| 			{"close", expectArgs{0xdeadbeef}, nil, nil}, | 			{"close", stub.ExpectArgs{0xdeadbeef}, nil, nil}, | ||||||
| 			{"openNew", expectArgs{"/host/proc/self/mountinfo"}, newConstFile(sampleMountinfoNix), nil}, | 			{"openNew", stub.ExpectArgs{"/host/proc/self/mountinfo"}, newConstFile(sampleMountinfoNix), nil}, | ||||||
| 			{"mount", expectArgs{"none", "/sysroot/nix", "", uintptr(0x209027), ""}, nil, nil}, | 			{"mount", stub.ExpectArgs{"none", "/sysroot/nix", "", uintptr(0x209027), ""}, nil, nil}, | ||||||
| 			{"mount", expectArgs{"none", "/sysroot/nix/.ro-store", "", uintptr(0x209027), ""}, nil, nil}, | 			{"mount", stub.ExpectArgs{"none", "/sysroot/nix/.ro-store", "", uintptr(0x209027), ""}, nil, nil}, | ||||||
| 			{"mount", expectArgs{"none", "/sysroot/nix/store", "", uintptr(0x209027), ""}, nil, nil}, | 			{"mount", stub.ExpectArgs{"none", "/sysroot/nix/store", "", uintptr(0x209027), ""}, nil, nil}, | ||||||
| 		}}, nil}, | 		}}, nil}, | ||||||
| 
 | 
 | ||||||
| 		{"success", func(k syscallDispatcher) error { | 		{"success", func(k syscallDispatcher) error { | ||||||
| 			return newProcPaths(k, hostPath).remount("/sysroot/.nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV) | 			return newProcPaths(k, hostPath).remount("/sysroot/.nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV) | ||||||
| 		}, [][]kexpect{{ | 		}, stub.Expect{Calls: []stub.Call{ | ||||||
| 			{"evalSymlinks", expectArgs{"/sysroot/.nix"}, "/sysroot/NIX", nil}, | 			{"evalSymlinks", stub.ExpectArgs{"/sysroot/.nix"}, "/sysroot/NIX", nil}, | ||||||
| 			{"verbosef", expectArgs{"target resolves to %q", []any{"/sysroot/NIX"}}, nil, nil}, | 			{"verbosef", stub.ExpectArgs{"target resolves to %q", []any{"/sysroot/NIX"}}, nil, nil}, | ||||||
| 			{"open", expectArgs{"/sysroot/NIX", 0x280000, uint32(0)}, 0xdeadbeef, nil}, | 			{"open", stub.ExpectArgs{"/sysroot/NIX", 0x280000, uint32(0)}, 0xdeadbeef, nil}, | ||||||
| 			{"readlink", expectArgs{"/host/proc/self/fd/3735928559"}, "/sysroot/nix", nil}, | 			{"readlink", stub.ExpectArgs{"/host/proc/self/fd/3735928559"}, "/sysroot/nix", nil}, | ||||||
| 			{"close", expectArgs{0xdeadbeef}, nil, nil}, | 			{"close", stub.ExpectArgs{0xdeadbeef}, nil, nil}, | ||||||
| 			{"openNew", expectArgs{"/host/proc/self/mountinfo"}, newConstFile(sampleMountinfoNix), nil}, | 			{"openNew", stub.ExpectArgs{"/host/proc/self/mountinfo"}, newConstFile(sampleMountinfoNix), nil}, | ||||||
| 			{"mount", expectArgs{"none", "/sysroot/nix", "", uintptr(0x209027), ""}, nil, nil}, | 			{"mount", stub.ExpectArgs{"none", "/sysroot/nix", "", uintptr(0x209027), ""}, nil, nil}, | ||||||
| 			{"mount", expectArgs{"none", "/sysroot/nix/.ro-store", "", uintptr(0x209027), ""}, nil, nil}, | 			{"mount", stub.ExpectArgs{"none", "/sysroot/nix/.ro-store", "", uintptr(0x209027), ""}, nil, nil}, | ||||||
| 			{"mount", expectArgs{"none", "/sysroot/nix/store", "", uintptr(0x209027), ""}, nil, nil}, | 			{"mount", stub.ExpectArgs{"none", "/sysroot/nix/store", "", uintptr(0x209027), ""}, nil, nil}, | ||||||
| 		}}, nil}, | 		}}, nil}, | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
| @ -221,18 +222,18 @@ func TestRemountWithFlags(t *testing.T) { | |||||||
| 	checkSimple(t, "remountWithFlags", []simpleTestCase{ | 	checkSimple(t, "remountWithFlags", []simpleTestCase{ | ||||||
| 		{"noop unmatched", func(k syscallDispatcher) error { | 		{"noop unmatched", func(k syscallDispatcher) error { | ||||||
| 			return remountWithFlags(k, &vfs.MountInfoNode{MountInfoEntry: &vfs.MountInfoEntry{VfsOptstr: "rw,relatime,cat"}}, 0) | 			return remountWithFlags(k, &vfs.MountInfoNode{MountInfoEntry: &vfs.MountInfoEntry{VfsOptstr: "rw,relatime,cat"}}, 0) | ||||||
| 		}, [][]kexpect{{ | 		}, stub.Expect{Calls: []stub.Call{ | ||||||
| 			{"verbosef", expectArgs{"unmatched vfs options: %q", []any{[]string{"cat"}}}, nil, nil}, | 			{"verbosef", stub.ExpectArgs{"unmatched vfs options: %q", []any{[]string{"cat"}}}, nil, nil}, | ||||||
| 		}}, nil}, | 		}}, nil}, | ||||||
| 
 | 
 | ||||||
| 		{"noop", func(k syscallDispatcher) error { | 		{"noop", func(k syscallDispatcher) error { | ||||||
| 			return remountWithFlags(k, &vfs.MountInfoNode{MountInfoEntry: &vfs.MountInfoEntry{VfsOptstr: "rw,relatime"}}, 0) | 			return remountWithFlags(k, &vfs.MountInfoNode{MountInfoEntry: &vfs.MountInfoEntry{VfsOptstr: "rw,relatime"}}, 0) | ||||||
| 		}, nil, nil}, | 		}, stub.Expect{}, nil}, | ||||||
| 
 | 
 | ||||||
| 		{"success", func(k syscallDispatcher) error { | 		{"success", func(k syscallDispatcher) error { | ||||||
| 			return remountWithFlags(k, &vfs.MountInfoNode{MountInfoEntry: &vfs.MountInfoEntry{VfsOptstr: "rw,relatime"}}, syscall.MS_RDONLY) | 			return remountWithFlags(k, &vfs.MountInfoNode{MountInfoEntry: &vfs.MountInfoEntry{VfsOptstr: "rw,relatime"}}, syscall.MS_RDONLY) | ||||||
| 		}, [][]kexpect{{ | 		}, stub.Expect{Calls: []stub.Call{ | ||||||
| 			{"mount", expectArgs{"none", "", "", uintptr(0x209021), ""}, nil, nil}, | 			{"mount", stub.ExpectArgs{"none", "", "", uintptr(0x209021), ""}, nil, nil}, | ||||||
| 		}}, nil}, | 		}}, nil}, | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
| @ -241,22 +242,22 @@ func TestMountTmpfs(t *testing.T) { | |||||||
| 	checkSimple(t, "mountTmpfs", []simpleTestCase{ | 	checkSimple(t, "mountTmpfs", []simpleTestCase{ | ||||||
| 		{"mkdirAll", func(k syscallDispatcher) error { | 		{"mkdirAll", func(k syscallDispatcher) error { | ||||||
| 			return mountTmpfs(k, "ephemeral", "/sysroot/run/user/1000", 0, 1<<10, 0700) | 			return mountTmpfs(k, "ephemeral", "/sysroot/run/user/1000", 0, 1<<10, 0700) | ||||||
| 		}, [][]kexpect{{ | 		}, stub.Expect{Calls: []stub.Call{ | ||||||
| 			{"mkdirAll", expectArgs{"/sysroot/run/user/1000", os.FileMode(0700)}, nil, errUnique}, | 			{"mkdirAll", stub.ExpectArgs{"/sysroot/run/user/1000", os.FileMode(0700)}, nil, stub.UniqueError(0)}, | ||||||
| 		}}, errUnique}, | 		}}, stub.UniqueError(0)}, | ||||||
| 
 | 
 | ||||||
| 		{"success no size", func(k syscallDispatcher) error { | 		{"success no size", func(k syscallDispatcher) error { | ||||||
| 			return mountTmpfs(k, "ephemeral", "/sysroot/run/user/1000", 0, 0, 0710) | 			return mountTmpfs(k, "ephemeral", "/sysroot/run/user/1000", 0, 0, 0710) | ||||||
| 		}, [][]kexpect{{ | 		}, stub.Expect{Calls: []stub.Call{ | ||||||
| 			{"mkdirAll", expectArgs{"/sysroot/run/user/1000", os.FileMode(0750)}, nil, nil}, | 			{"mkdirAll", stub.ExpectArgs{"/sysroot/run/user/1000", os.FileMode(0750)}, nil, nil}, | ||||||
| 			{"mount", expectArgs{"ephemeral", "/sysroot/run/user/1000", "tmpfs", uintptr(0), "mode=0710"}, nil, nil}, | 			{"mount", stub.ExpectArgs{"ephemeral", "/sysroot/run/user/1000", "tmpfs", uintptr(0), "mode=0710"}, nil, nil}, | ||||||
| 		}}, nil}, | 		}}, nil}, | ||||||
| 
 | 
 | ||||||
| 		{"success", func(k syscallDispatcher) error { | 		{"success", func(k syscallDispatcher) error { | ||||||
| 			return mountTmpfs(k, "ephemeral", "/sysroot/run/user/1000", 0, 1<<10, 0700) | 			return mountTmpfs(k, "ephemeral", "/sysroot/run/user/1000", 0, 1<<10, 0700) | ||||||
| 		}, [][]kexpect{{ | 		}, stub.Expect{Calls: []stub.Call{ | ||||||
| 			{"mkdirAll", expectArgs{"/sysroot/run/user/1000", os.FileMode(0700)}, nil, nil}, | 			{"mkdirAll", stub.ExpectArgs{"/sysroot/run/user/1000", os.FileMode(0700)}, nil, nil}, | ||||||
| 			{"mount", expectArgs{"ephemeral", "/sysroot/run/user/1000", "tmpfs", uintptr(0), "mode=0700,size=1024"}, nil, nil}, | 			{"mount", stub.ExpectArgs{"ephemeral", "/sysroot/run/user/1000", "tmpfs", uintptr(0), "mode=0700,size=1024"}, nil, nil}, | ||||||
| 		}}, nil}, | 		}}, nil}, | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										37
									
								
								container/stub/call.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								container/stub/call.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,37 @@ | |||||||
|  | package stub | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"slices" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // ExpectArgs is an array primarily for storing expected function arguments. | ||||||
|  | // Its actual use is defined by the implementation. | ||||||
|  | type ExpectArgs = [5]any | ||||||
|  | 
 | ||||||
|  | // An Expect stores expected calls of a goroutine. | ||||||
|  | type Expect struct { | ||||||
|  | 	Calls []Call | ||||||
|  | 
 | ||||||
|  | 	// Tracks are handed out to descendant goroutines in order. | ||||||
|  | 	Tracks []Expect | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // A Call holds expected arguments of a function call and its outcome. | ||||||
|  | type Call struct { | ||||||
|  | 	// Name is the function Name of this call. Must be unique. | ||||||
|  | 	Name string | ||||||
|  | 	// Args are the expected arguments of this Call. | ||||||
|  | 	Args ExpectArgs | ||||||
|  | 	// Ret is the return value of this Call. | ||||||
|  | 	Ret any | ||||||
|  | 	// Err is the returned error of this Call. | ||||||
|  | 	Err error | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Error returns [Call.Err] if all arguments are true, or [ErrCheck] otherwise. | ||||||
|  | func (k *Call) Error(ok ...bool) error { | ||||||
|  | 	if !slices.Contains(ok, false) { | ||||||
|  | 		return k.Err | ||||||
|  | 	} | ||||||
|  | 	return ErrCheck | ||||||
|  | } | ||||||
							
								
								
									
										23
									
								
								container/stub/call_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								container/stub/call_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | |||||||
|  | package stub_test | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"reflect" | ||||||
|  | 	"testing" | ||||||
|  | 
 | ||||||
|  | 	"hakurei.app/container/stub" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func TestCallError(t *testing.T) { | ||||||
|  | 	t.Run("contains false", func(t *testing.T) { | ||||||
|  | 		if err := new(stub.Call).Error(true, false, true); !reflect.DeepEqual(err, stub.ErrCheck) { | ||||||
|  | 			t.Errorf("Error: %#v, want %#v", err, stub.ErrCheck) | ||||||
|  | 		} | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	t.Run("passthrough", func(t *testing.T) { | ||||||
|  | 		wantErr := stub.UniqueError(0xbabe) | ||||||
|  | 		if err := (&stub.Call{Err: wantErr}).Error(true); !reflect.DeepEqual(err, wantErr) { | ||||||
|  | 			t.Errorf("Error: %#v, want %#v", err, wantErr) | ||||||
|  | 		} | ||||||
|  | 	}) | ||||||
|  | } | ||||||
							
								
								
									
										25
									
								
								container/stub/errors.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								container/stub/errors.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,25 @@ | |||||||
|  | package stub | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"errors" | ||||||
|  | 	"strconv" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | var ( | ||||||
|  | 	ErrCheck = errors.New("one or more arguments did not match") | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // UniqueError is an error that only equivalates to other [UniqueError] with the same magic value. | ||||||
|  | type UniqueError uintptr | ||||||
|  | 
 | ||||||
|  | func (e UniqueError) Error() string { | ||||||
|  | 	return "unique error " + strconv.Itoa(int(e)) + " injected by the test suite" | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (e UniqueError) Is(target error) bool { | ||||||
|  | 	var u UniqueError | ||||||
|  | 	if !errors.As(target, &u) { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 	return e == u | ||||||
|  | } | ||||||
							
								
								
									
										35
									
								
								container/stub/errors_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								container/stub/errors_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,35 @@ | |||||||
|  | package stub_test | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"errors" | ||||||
|  | 	"syscall" | ||||||
|  | 	"testing" | ||||||
|  | 
 | ||||||
|  | 	"hakurei.app/container/stub" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func TestUniqueError(t *testing.T) { | ||||||
|  | 	t.Run("format", func(t *testing.T) { | ||||||
|  | 		want := "unique error 2989 injected by the test suite" | ||||||
|  | 		if got := stub.UniqueError(0xbad).Error(); got != want { | ||||||
|  | 			t.Errorf("Error: %q, want %q", got, want) | ||||||
|  | 		} | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	t.Run("is", func(t *testing.T) { | ||||||
|  | 		t.Run("type", func(t *testing.T) { | ||||||
|  | 			if errors.Is(stub.UniqueError(0), syscall.ENOTRECOVERABLE) { | ||||||
|  | 				t.Error("Is: unexpected true") | ||||||
|  | 			} | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		t.Run("val", func(t *testing.T) { | ||||||
|  | 			if errors.Is(stub.UniqueError(0), stub.UniqueError(1)) { | ||||||
|  | 				t.Error("Is: unexpected true") | ||||||
|  | 			} | ||||||
|  | 			if !errors.Is(stub.UniqueError(0xbad), stub.UniqueError(0xbad)) { | ||||||
|  | 				t.Error("Is: unexpected false") | ||||||
|  | 			} | ||||||
|  | 		}) | ||||||
|  | 	}) | ||||||
|  | } | ||||||
							
								
								
									
										15
									
								
								container/stub/exit.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								container/stub/exit.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | |||||||
|  | package stub | ||||||
|  | 
 | ||||||
|  | // PanicExit is a magic panic value treated as a simulated exit. | ||||||
|  | const PanicExit = 0xdeadbeef | ||||||
|  | 
 | ||||||
|  | // HandleExit must be deferred before calling with the stub. | ||||||
|  | func HandleExit() { | ||||||
|  | 	r := recover() | ||||||
|  | 	if r == PanicExit { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if r != nil { | ||||||
|  | 		panic(r) | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										30
									
								
								container/stub/exit_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								container/stub/exit_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | |||||||
|  | package stub_test | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"testing" | ||||||
|  | 
 | ||||||
|  | 	"hakurei.app/container/stub" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func TestHandleExit(t *testing.T) { | ||||||
|  | 	t.Run("exit", func(t *testing.T) { | ||||||
|  | 		defer stub.HandleExit() | ||||||
|  | 		panic(stub.PanicExit) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	t.Run("nil", func(t *testing.T) { | ||||||
|  | 		defer stub.HandleExit() | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	t.Run("passthrough", func(t *testing.T) { | ||||||
|  | 		defer func() { | ||||||
|  | 			want := 0xcafebabe | ||||||
|  | 			if r := recover(); r != want { | ||||||
|  | 				t.Errorf("recover: %v, want %v", r, want) | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 		}() | ||||||
|  | 		defer stub.HandleExit() | ||||||
|  | 		panic(0xcafebabe) | ||||||
|  | 	}) | ||||||
|  | } | ||||||
							
								
								
									
										141
									
								
								container/stub/stub.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								container/stub/stub.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,141 @@ | |||||||
|  | // Package stub provides function call level stubbing and validation | ||||||
|  | // for library functions that are impossible to check otherwise. | ||||||
|  | package stub | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"reflect" | ||||||
|  | 	"sync" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // this should prevent stub from being inadvertently imported outside tests | ||||||
|  | var _ = func() { | ||||||
|  | 	if !testing.Testing() { | ||||||
|  | 		panic("stub imported while not in a test") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const ( | ||||||
|  | 	// A CallSeparator denotes an injected separation between two groups of calls. | ||||||
|  | 	CallSeparator = "\x00" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // A Stub is a collection of tracks of expected calls. | ||||||
|  | type Stub[K any] struct { | ||||||
|  | 	testing.TB | ||||||
|  | 
 | ||||||
|  | 	// makeK creates a new K for a descendant [Stub]. | ||||||
|  | 	// This function may be called concurrently. | ||||||
|  | 	makeK func(s *Stub[K]) K | ||||||
|  | 
 | ||||||
|  | 	// want is a hierarchy of expected calls. | ||||||
|  | 	want Expect | ||||||
|  | 	// pos is the current position in [Expect.Calls]. | ||||||
|  | 	pos int | ||||||
|  | 	// goroutine counts the number of goroutines created by this [Stub]. | ||||||
|  | 	goroutine int | ||||||
|  | 	// sub stores the addresses of descendant [Stub] created by New. | ||||||
|  | 	sub []*Stub[K] | ||||||
|  | 	// wg waits for all descendants to complete. | ||||||
|  | 	wg *sync.WaitGroup | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // New creates a root [Stub]. | ||||||
|  | func New[K any](tb testing.TB, makeK func(s *Stub[K]) K, want Expect) *Stub[K] { | ||||||
|  | 	return &Stub[K]{TB: tb, makeK: makeK, want: want, wg: new(sync.WaitGroup)} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // New calls f in a new goroutine | ||||||
|  | func (s *Stub[K]) New(f func(k K)) { | ||||||
|  | 	s.Helper() | ||||||
|  | 
 | ||||||
|  | 	s.Expects("New") | ||||||
|  | 	if len(s.want.Tracks) <= s.goroutine { | ||||||
|  | 		s.Fatal("New: track overrun") | ||||||
|  | 	} | ||||||
|  | 	ds := &Stub[K]{TB: s.TB, makeK: s.makeK, want: s.want.Tracks[s.goroutine], wg: s.wg} | ||||||
|  | 	s.goroutine++ | ||||||
|  | 	s.sub = append(s.sub, ds) | ||||||
|  | 	s.wg.Add(1) | ||||||
|  | 	go func() { | ||||||
|  | 		s.Helper() | ||||||
|  | 
 | ||||||
|  | 		defer s.wg.Done() | ||||||
|  | 		defer HandleExit() | ||||||
|  | 		f(s.makeK(ds)) | ||||||
|  | 	}() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Pos returns the current position of [Stub] in its [Expect.Calls] | ||||||
|  | func (s *Stub[K]) Pos() int { return s.pos } | ||||||
|  | 
 | ||||||
|  | // Len returns the length of [Expect.Calls]. | ||||||
|  | func (s *Stub[K]) Len() int { return len(s.want.Calls) } | ||||||
|  | 
 | ||||||
|  | // VisitIncomplete calls f on an incomplete s and all its descendants. | ||||||
|  | func (s *Stub[K]) VisitIncomplete(f func(s *Stub[K])) { | ||||||
|  | 	s.Helper() | ||||||
|  | 	s.wg.Wait() | ||||||
|  | 
 | ||||||
|  | 	if s.want.Calls != nil && len(s.want.Calls) != s.pos { | ||||||
|  | 		f(s) | ||||||
|  | 	} | ||||||
|  | 	for _, ds := range s.sub { | ||||||
|  | 		ds.VisitIncomplete(f) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Expects checks the name of and returns the current [Call] and advances pos. | ||||||
|  | func (s *Stub[K]) Expects(name string) (expect *Call) { | ||||||
|  | 	s.Helper() | ||||||
|  | 
 | ||||||
|  | 	if len(s.want.Calls) == s.pos { | ||||||
|  | 		s.Fatal("Expects: advancing beyond expected calls") | ||||||
|  | 	} | ||||||
|  | 	expect = &s.want.Calls[s.pos] | ||||||
|  | 	if name != expect.Name { | ||||||
|  | 		if expect.Name == CallSeparator { | ||||||
|  | 			s.Fatalf("Expects: func = %s, separator overrun", name) | ||||||
|  | 		} | ||||||
|  | 		if name == CallSeparator { | ||||||
|  | 			s.Fatalf("Expects: separator, want %s", expect.Name) | ||||||
|  | 		} | ||||||
|  | 		s.Fatalf("Expects: func = %s, want %s", name, expect.Name) | ||||||
|  | 	} | ||||||
|  | 	s.pos++ | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // CheckArg checks an argument comparable with the == operator. Avoid using this with pointers. | ||||||
|  | func CheckArg[T comparable, K any](s *Stub[K], arg string, got T, n int) bool { | ||||||
|  | 	s.Helper() | ||||||
|  | 
 | ||||||
|  | 	pos := s.pos - 1 | ||||||
|  | 	if pos < 0 || pos >= len(s.want.Calls) { | ||||||
|  | 		panic("invalid call to CheckArg") | ||||||
|  | 	} | ||||||
|  | 	expect := s.want.Calls[pos] | ||||||
|  | 	want, ok := expect.Args[n].(T) | ||||||
|  | 	if !ok || got != want { | ||||||
|  | 		s.Errorf("%s: %s = %#v, want %#v (%d)", expect.Name, arg, got, want, pos) | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 	return true | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // CheckArgReflect checks an argument of any type. | ||||||
|  | func CheckArgReflect[K any](s *Stub[K], arg string, got any, n int) bool { | ||||||
|  | 	s.Helper() | ||||||
|  | 
 | ||||||
|  | 	pos := s.pos - 1 | ||||||
|  | 	if pos < 0 || pos >= len(s.want.Calls) { | ||||||
|  | 		panic("invalid call to CheckArgReflect") | ||||||
|  | 	} | ||||||
|  | 	expect := s.want.Calls[pos] | ||||||
|  | 	want := expect.Args[n] | ||||||
|  | 	if !reflect.DeepEqual(got, want) { | ||||||
|  | 		s.Errorf("%s: %s = %#v, want %#v (%d)", expect.Name, arg, got, want, pos) | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 	return true | ||||||
|  | } | ||||||
							
								
								
									
										265
									
								
								container/stub/stub_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										265
									
								
								container/stub/stub_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,265 @@ | |||||||
|  | package stub | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"reflect" | ||||||
|  | 	"sync/atomic" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // stubHolder embeds [Stub]. | ||||||
|  | type stubHolder struct{ *Stub[stubHolder] } | ||||||
|  | 
 | ||||||
|  | // overrideT allows some methods of [testing.T] to be overridden. | ||||||
|  | type overrideT struct { | ||||||
|  | 	*testing.T | ||||||
|  | 
 | ||||||
|  | 	fatal  atomic.Pointer[func(args ...any)] | ||||||
|  | 	fatalf atomic.Pointer[func(format string, args ...any)] | ||||||
|  | 	errorf atomic.Pointer[func(format string, args ...any)] | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (t *overrideT) Fatal(args ...any) { | ||||||
|  | 	fp := t.fatal.Load() | ||||||
|  | 	if fp == nil || *fp == nil { | ||||||
|  | 		t.T.Fatal(args...) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	(*fp)(args...) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (t *overrideT) Fatalf(format string, args ...any) { | ||||||
|  | 	fp := t.fatalf.Load() | ||||||
|  | 	if fp == nil || *fp == nil { | ||||||
|  | 		t.T.Fatalf(format, args...) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	(*fp)(format, args...) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (t *overrideT) Errorf(format string, args ...any) { | ||||||
|  | 	fp := t.errorf.Load() | ||||||
|  | 	if fp == nil || *fp == nil { | ||||||
|  | 		t.T.Errorf(format, args...) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	(*fp)(format, args...) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestStub(t *testing.T) { | ||||||
|  | 	t.Run("new", func(t *testing.T) { | ||||||
|  | 		t.Run("success", func(t *testing.T) { | ||||||
|  | 			s := New(t, func(s *Stub[stubHolder]) stubHolder { return stubHolder{s} }, Expect{Calls: []Call{ | ||||||
|  | 				{"New", ExpectArgs{}, nil, nil}, | ||||||
|  | 			}, Tracks: []Expect{{Calls: []Call{ | ||||||
|  | 				{"done", ExpectArgs{0xbabe}, nil, nil}, | ||||||
|  | 			}}}}) | ||||||
|  | 
 | ||||||
|  | 			s.New(func(k stubHolder) { | ||||||
|  | 				expect := k.Expects("done") | ||||||
|  | 				if expect.Name != "done" { | ||||||
|  | 					t.Errorf("New: Name = %s, want done", expect.Name) | ||||||
|  | 				} | ||||||
|  | 				if expect.Args != (ExpectArgs{0xbabe}) { | ||||||
|  | 					t.Errorf("New: Args = %#v", expect.Args) | ||||||
|  | 				} | ||||||
|  | 				if expect.Ret != nil { | ||||||
|  | 					t.Errorf("New: Ret = %#v", expect.Ret) | ||||||
|  | 				} | ||||||
|  | 				if expect.Err != nil { | ||||||
|  | 					t.Errorf("New: Err = %#v", expect.Err) | ||||||
|  | 				} | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			if pos := s.Pos(); pos != 1 { | ||||||
|  | 				t.Errorf("Pos: %d, want 1", pos) | ||||||
|  | 			} | ||||||
|  | 			if l := s.Len(); l != 1 { | ||||||
|  | 				t.Errorf("Len: %d, want 1", l) | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			s.VisitIncomplete(func(s *Stub[stubHolder]) { panic("unreachable") }) | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		t.Run("overrun", func(t *testing.T) { | ||||||
|  | 			ot := &overrideT{T: t} | ||||||
|  | 			ot.fatal.Store(checkFatal(t, "New: track overrun")) | ||||||
|  | 			s := New(ot, func(s *Stub[stubHolder]) stubHolder { return stubHolder{s} }, Expect{Calls: []Call{ | ||||||
|  | 				{"New", ExpectArgs{}, nil, nil}, | ||||||
|  | 				{"panic", ExpectArgs{"unreachable"}, nil, nil}, | ||||||
|  | 			}}) | ||||||
|  | 			func() { defer HandleExit(); s.New(func(k stubHolder) { panic("unreachable") }) }() | ||||||
|  | 
 | ||||||
|  | 			var visit int | ||||||
|  | 			s.VisitIncomplete(func(s *Stub[stubHolder]) { | ||||||
|  | 				visit++ | ||||||
|  | 				if visit > 1 { | ||||||
|  | 					panic("unexpected visit count") | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				want := Call{"panic", ExpectArgs{"unreachable"}, nil, nil} | ||||||
|  | 				if got := s.want.Calls[s.pos]; !reflect.DeepEqual(got, want) { | ||||||
|  | 					t.Errorf("VisitIncomplete: %#v, want %#v", got, want) | ||||||
|  | 				} | ||||||
|  | 			}) | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		t.Run("expects", func(t *testing.T) { | ||||||
|  | 			t.Run("overrun", func(t *testing.T) { | ||||||
|  | 				ot := &overrideT{T: t} | ||||||
|  | 				ot.fatal.Store(checkFatal(t, "Expects: advancing beyond expected calls")) | ||||||
|  | 				s := New(ot, func(s *Stub[stubHolder]) stubHolder { return stubHolder{s} }, Expect{}) | ||||||
|  | 				func() { defer HandleExit(); s.Expects("unreachable") }() | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			t.Run("separator", func(t *testing.T) { | ||||||
|  | 				t.Run("overrun", func(t *testing.T) { | ||||||
|  | 					ot := &overrideT{T: t} | ||||||
|  | 					ot.fatalf.Store(checkFatalf(t, "Expects: func = %s, separator overrun", "meow")) | ||||||
|  | 					s := New(ot, func(s *Stub[stubHolder]) stubHolder { return stubHolder{s} }, Expect{Calls: []Call{ | ||||||
|  | 						{CallSeparator, ExpectArgs{}, nil, nil}, | ||||||
|  | 					}}) | ||||||
|  | 					func() { defer HandleExit(); s.Expects("meow") }() | ||||||
|  | 				}) | ||||||
|  | 
 | ||||||
|  | 				t.Run("mismatch", func(t *testing.T) { | ||||||
|  | 					ot := &overrideT{T: t} | ||||||
|  | 					ot.fatalf.Store(checkFatalf(t, "Expects: separator, want %s", "panic")) | ||||||
|  | 					s := New(ot, func(s *Stub[stubHolder]) stubHolder { return stubHolder{s} }, Expect{Calls: []Call{ | ||||||
|  | 						{"panic", ExpectArgs{}, nil, nil}, | ||||||
|  | 					}}) | ||||||
|  | 					func() { defer HandleExit(); s.Expects(CallSeparator) }() | ||||||
|  | 				}) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			t.Run("mismatch", func(t *testing.T) { | ||||||
|  | 				ot := &overrideT{T: t} | ||||||
|  | 				ot.fatalf.Store(checkFatalf(t, "Expects: func = %s, want %s", "meow", "nya")) | ||||||
|  | 				s := New(ot, func(s *Stub[stubHolder]) stubHolder { return stubHolder{s} }, Expect{Calls: []Call{ | ||||||
|  | 					{"nya", ExpectArgs{}, nil, nil}, | ||||||
|  | 				}}) | ||||||
|  | 				func() { defer HandleExit(); s.Expects("meow") }() | ||||||
|  | 			}) | ||||||
|  | 		}) | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestCheckArg(t *testing.T) { | ||||||
|  | 	t.Run("oob negative", func(t *testing.T) { | ||||||
|  | 		defer func() { | ||||||
|  | 			want := "invalid call to CheckArg" | ||||||
|  | 			if r := recover(); r != want { | ||||||
|  | 				t.Errorf("recover: %v, want %v", r, want) | ||||||
|  | 			} | ||||||
|  | 		}() | ||||||
|  | 		s := New(t, func(s *Stub[stubHolder]) stubHolder { return stubHolder{s} }, Expect{}) | ||||||
|  | 		CheckArg(s, "unreachable", struct{}{}, 0) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	ot := &overrideT{T: t} | ||||||
|  | 	s := New(ot, func(s *Stub[stubHolder]) stubHolder { return stubHolder{s} }, Expect{Calls: []Call{ | ||||||
|  | 		{"panic", ExpectArgs{PanicExit}, nil, nil}, | ||||||
|  | 		{"meow", ExpectArgs{-1}, nil, nil}, | ||||||
|  | 	}}) | ||||||
|  | 	t.Run("match", func(t *testing.T) { | ||||||
|  | 		s.Expects("panic") | ||||||
|  | 		if !CheckArg(s, "v", PanicExit, 0) { | ||||||
|  | 			t.Errorf("CheckArg: unexpected false") | ||||||
|  | 		} | ||||||
|  | 	}) | ||||||
|  | 	t.Run("mismatch", func(t *testing.T) { | ||||||
|  | 		defer HandleExit() | ||||||
|  | 		s.Expects("meow") | ||||||
|  | 		ot.errorf.Store(checkFatalf(t, "%s: %s = %#v, want %#v (%d)", "meow", "time", 0, -1, 1)) | ||||||
|  | 		if CheckArg(s, "time", 0, 0) { | ||||||
|  | 			t.Errorf("CheckArg: unexpected true") | ||||||
|  | 		} | ||||||
|  | 	}) | ||||||
|  | 	t.Run("oob", func(t *testing.T) { | ||||||
|  | 		s.pos++ | ||||||
|  | 		defer func() { | ||||||
|  | 			want := "invalid call to CheckArg" | ||||||
|  | 			if r := recover(); r != want { | ||||||
|  | 				t.Errorf("recover: %v, want %v", r, want) | ||||||
|  | 			} | ||||||
|  | 		}() | ||||||
|  | 		CheckArg(s, "unreachable", struct{}{}, 0) | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestCheckArgReflect(t *testing.T) { | ||||||
|  | 	t.Run("oob lower", func(t *testing.T) { | ||||||
|  | 		defer func() { | ||||||
|  | 			want := "invalid call to CheckArgReflect" | ||||||
|  | 			if r := recover(); r != want { | ||||||
|  | 				t.Errorf("recover: %v, want %v", r, want) | ||||||
|  | 			} | ||||||
|  | 		}() | ||||||
|  | 		s := New(t, func(s *Stub[stubHolder]) stubHolder { return stubHolder{s} }, Expect{}) | ||||||
|  | 		CheckArgReflect(s, "unreachable", struct{}{}, 0) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	ot := &overrideT{T: t} | ||||||
|  | 	s := New(ot, func(s *Stub[stubHolder]) stubHolder { return stubHolder{s} }, Expect{Calls: []Call{ | ||||||
|  | 		{"panic", ExpectArgs{PanicExit}, nil, nil}, | ||||||
|  | 		{"meow", ExpectArgs{-1}, nil, nil}, | ||||||
|  | 	}}) | ||||||
|  | 	t.Run("match", func(t *testing.T) { | ||||||
|  | 		s.Expects("panic") | ||||||
|  | 		if !CheckArgReflect(s, "v", PanicExit, 0) { | ||||||
|  | 			t.Errorf("CheckArgReflect: unexpected false") | ||||||
|  | 		} | ||||||
|  | 	}) | ||||||
|  | 	t.Run("mismatch", func(t *testing.T) { | ||||||
|  | 		defer HandleExit() | ||||||
|  | 		s.Expects("meow") | ||||||
|  | 		ot.errorf.Store(checkFatalf(t, "%s: %s = %#v, want %#v (%d)", "meow", "time", 0, -1, 1)) | ||||||
|  | 		if CheckArgReflect(s, "time", 0, 0) { | ||||||
|  | 			t.Errorf("CheckArgReflect: unexpected true") | ||||||
|  | 		} | ||||||
|  | 	}) | ||||||
|  | 	t.Run("oob", func(t *testing.T) { | ||||||
|  | 		s.pos++ | ||||||
|  | 		defer func() { | ||||||
|  | 			want := "invalid call to CheckArgReflect" | ||||||
|  | 			if r := recover(); r != want { | ||||||
|  | 				t.Errorf("recover: %v, want %v", r, want) | ||||||
|  | 			} | ||||||
|  | 		}() | ||||||
|  | 		CheckArgReflect(s, "unreachable", struct{}{}, 0) | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func checkFatal(t *testing.T, wantArgs ...any) *func(args ...any) { | ||||||
|  | 	var called bool | ||||||
|  | 	f := func(args ...any) { | ||||||
|  | 		if called { | ||||||
|  | 			panic("invalid call to fatal") | ||||||
|  | 		} | ||||||
|  | 		called = true | ||||||
|  | 
 | ||||||
|  | 		if !reflect.DeepEqual(args, wantArgs) { | ||||||
|  | 			t.Errorf("Fatal: %#v, want %#v", args, wantArgs) | ||||||
|  | 		} | ||||||
|  | 		panic(PanicExit) | ||||||
|  | 	} | ||||||
|  | 	return &f | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func checkFatalf(t *testing.T, wantFormat string, wantArgs ...any) *func(format string, args ...any) { | ||||||
|  | 	var called bool | ||||||
|  | 	f := func(format string, args ...any) { | ||||||
|  | 		if called { | ||||||
|  | 			panic("invalid call to fatalf") | ||||||
|  | 		} | ||||||
|  | 		called = true | ||||||
|  | 
 | ||||||
|  | 		if format != wantFormat { | ||||||
|  | 			t.Errorf("Fatalf: format = %q, want %q", format, wantFormat) | ||||||
|  | 		} | ||||||
|  | 		if !reflect.DeepEqual(args, wantArgs) { | ||||||
|  | 			t.Errorf("Fatalf: args = %#v, want %#v", args, wantArgs) | ||||||
|  | 		} | ||||||
|  | 		panic(PanicExit) | ||||||
|  | 	} | ||||||
|  | 	return &f | ||||||
|  | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user