All checks were successful
		
		
	
	Test / Create distribution (push) Successful in 25s
				
			Test / Hakurei (push) Successful in 44s
				
			Test / Sandbox (push) Successful in 41s
				
			Test / Hakurei (race detector) (push) Successful in 44s
				
			Test / Sandbox (race detector) (push) Successful in 41s
				
			Test / Hpkg (push) Successful in 41s
				
			Test / Flake checks (push) Successful in 1m24s
				
			Most tests already had no global state, however parallel was never enabled. This change enables it for all applicable tests. Signed-off-by: Ophestra <cat@gensokyo.uk>
		
			
				
	
	
		
			356 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			356 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package hst_test
 | 
						|
 | 
						|
import (
 | 
						|
	"encoding/json"
 | 
						|
	"errors"
 | 
						|
	"os"
 | 
						|
	"reflect"
 | 
						|
	"strings"
 | 
						|
	"syscall"
 | 
						|
	"testing"
 | 
						|
 | 
						|
	"hakurei.app/container"
 | 
						|
	"hakurei.app/container/check"
 | 
						|
	"hakurei.app/hst"
 | 
						|
)
 | 
						|
 | 
						|
func TestFilesystemConfigJSON(t *testing.T) {
 | 
						|
	t.Parallel()
 | 
						|
 | 
						|
	testCases := []struct {
 | 
						|
		name string
 | 
						|
		want hst.FilesystemConfigJSON
 | 
						|
 | 
						|
		wantErr     error
 | 
						|
		data, sData string
 | 
						|
	}{
 | 
						|
		{"nil", hst.FilesystemConfigJSON{FilesystemConfig: nil}, hst.ErrFSNull,
 | 
						|
			`null`, `{"fs":null,"magic":3236757504}`},
 | 
						|
 | 
						|
		{"bad type", hst.FilesystemConfigJSON{FilesystemConfig: stubFS{"cat"}},
 | 
						|
			hst.FSTypeError("cat"),
 | 
						|
			`{"type":"cat","meow":true}`, `{"fs":{"type":"cat","meow":true},"magic":3236757504}`},
 | 
						|
 | 
						|
		{"bad impl bind", hst.FilesystemConfigJSON{FilesystemConfig: stubFS{"bind"}},
 | 
						|
			hst.FSImplError{Value: stubFS{"bind"}},
 | 
						|
			"\x00", "\x00"},
 | 
						|
 | 
						|
		{"bad impl ephemeral", hst.FilesystemConfigJSON{FilesystemConfig: stubFS{"ephemeral"}},
 | 
						|
			hst.FSImplError{Value: stubFS{"ephemeral"}},
 | 
						|
			"\x00", "\x00"},
 | 
						|
 | 
						|
		{"bad impl overlay", hst.FilesystemConfigJSON{FilesystemConfig: stubFS{"overlay"}},
 | 
						|
			hst.FSImplError{Value: stubFS{"overlay"}},
 | 
						|
			"\x00", "\x00"},
 | 
						|
 | 
						|
		{"bind", hst.FilesystemConfigJSON{
 | 
						|
			FilesystemConfig: &hst.FSBind{
 | 
						|
				Target:   m("/etc"),
 | 
						|
				Source:   m("/mnt/etc"),
 | 
						|
				Optional: true,
 | 
						|
			},
 | 
						|
		}, nil,
 | 
						|
			`{"type":"bind","dst":"/etc","src":"/mnt/etc","optional":true}`,
 | 
						|
			`{"fs":{"type":"bind","dst":"/etc","src":"/mnt/etc","optional":true},"magic":3236757504}`},
 | 
						|
 | 
						|
		{"ephemeral", hst.FilesystemConfigJSON{
 | 
						|
			FilesystemConfig: &hst.FSEphemeral{
 | 
						|
				Target: m("/run/user/65534"),
 | 
						|
				Write:  true,
 | 
						|
				Size:   1 << 10,
 | 
						|
				Perm:   0700,
 | 
						|
			},
 | 
						|
		}, nil,
 | 
						|
			`{"type":"ephemeral","dst":"/run/user/65534","write":true,"size":1024,"perm":448}`,
 | 
						|
			`{"fs":{"type":"ephemeral","dst":"/run/user/65534","write":true,"size":1024,"perm":448},"magic":3236757504}`},
 | 
						|
 | 
						|
		{"overlay", hst.FilesystemConfigJSON{
 | 
						|
			FilesystemConfig: &hst.FSOverlay{
 | 
						|
				Target: m("/nix/store"),
 | 
						|
				Lower:  ms("/mnt-root/nix/.ro-store"),
 | 
						|
				Upper:  m("/mnt-root/nix/.rw-store/upper"),
 | 
						|
				Work:   m("/mnt-root/nix/.rw-store/work"),
 | 
						|
			},
 | 
						|
		}, nil,
 | 
						|
			`{"type":"overlay","dst":"/nix/store","lower":["/mnt-root/nix/.ro-store"],"upper":"/mnt-root/nix/.rw-store/upper","work":"/mnt-root/nix/.rw-store/work"}`,
 | 
						|
			`{"fs":{"type":"overlay","dst":"/nix/store","lower":["/mnt-root/nix/.ro-store"],"upper":"/mnt-root/nix/.rw-store/upper","work":"/mnt-root/nix/.rw-store/work"},"magic":3236757504}`},
 | 
						|
 | 
						|
		{"link", hst.FilesystemConfigJSON{
 | 
						|
			FilesystemConfig: &hst.FSLink{
 | 
						|
				Target:      m("/run/current-system"),
 | 
						|
				Linkname:    "/run/current-system",
 | 
						|
				Dereference: true,
 | 
						|
			},
 | 
						|
		}, nil,
 | 
						|
			`{"type":"link","dst":"/run/current-system","linkname":"/run/current-system","dereference":true}`,
 | 
						|
			`{"fs":{"type":"link","dst":"/run/current-system","linkname":"/run/current-system","dereference":true},"magic":3236757504}`},
 | 
						|
	}
 | 
						|
 | 
						|
	for _, tc := range testCases {
 | 
						|
		t.Run(tc.name, func(t *testing.T) {
 | 
						|
			t.Parallel()
 | 
						|
 | 
						|
			t.Run("marshal", func(t *testing.T) {
 | 
						|
				t.Parallel()
 | 
						|
				wantErr := tc.wantErr
 | 
						|
				if errors.As(wantErr, new(hst.FSTypeError)) {
 | 
						|
					// for unsupported implementation tc
 | 
						|
					wantErr = hst.FSImplError{Value: stubFS{"cat"}}
 | 
						|
				}
 | 
						|
 | 
						|
				{
 | 
						|
					d, err := json.Marshal(&tc.want)
 | 
						|
					if !errors.Is(err, wantErr) {
 | 
						|
						t.Errorf("Marshal: error = %v, want %v", err, wantErr)
 | 
						|
					}
 | 
						|
					if wantErr != nil {
 | 
						|
						goto checkSMarshal
 | 
						|
					}
 | 
						|
					if string(d) != tc.data {
 | 
						|
						t.Errorf("Marshal:\n%s\nwant:\n%s", string(d), tc.data)
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
			checkSMarshal:
 | 
						|
				{
 | 
						|
					d, err := json.Marshal(&sCheck{tc.want, syscall.MS_MGC_VAL})
 | 
						|
					if !errors.Is(err, wantErr) {
 | 
						|
						t.Errorf("Marshal: error = %v, want %v", err, wantErr)
 | 
						|
					}
 | 
						|
					if wantErr != nil {
 | 
						|
						return
 | 
						|
					}
 | 
						|
					if string(d) != tc.sData {
 | 
						|
						t.Errorf("Marshal:\n%s\nwant:\n%s", string(d), tc.sData)
 | 
						|
					}
 | 
						|
				}
 | 
						|
			})
 | 
						|
 | 
						|
			t.Run("unmarshal", func(t *testing.T) {
 | 
						|
				t.Parallel()
 | 
						|
				if tc.data == "\x00" && tc.sData == "\x00" {
 | 
						|
					if errors.As(tc.wantErr, new(hst.FSImplError)) {
 | 
						|
						// this error is only returned on marshal
 | 
						|
						return
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
				{
 | 
						|
					var got hst.FilesystemConfigJSON
 | 
						|
					err := json.Unmarshal([]byte(tc.data), &got)
 | 
						|
					if !errors.Is(err, tc.wantErr) {
 | 
						|
						t.Errorf("Unmarshal: error = %v, want %v", err, tc.wantErr)
 | 
						|
					}
 | 
						|
					if tc.wantErr != nil {
 | 
						|
						goto checkSUnmarshal
 | 
						|
					}
 | 
						|
					if !reflect.DeepEqual(&tc.want, &got) {
 | 
						|
						t.Errorf("Unmarshal: %#v, want %#v", &tc.want, &got)
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
			checkSUnmarshal:
 | 
						|
				{
 | 
						|
					var got sCheck
 | 
						|
					err := json.Unmarshal([]byte(tc.sData), &got)
 | 
						|
					if !errors.Is(err, tc.wantErr) {
 | 
						|
						t.Errorf("Unmarshal: error = %v, want %v", err, tc.wantErr)
 | 
						|
					}
 | 
						|
					if tc.wantErr != nil {
 | 
						|
						return
 | 
						|
					}
 | 
						|
					want := sCheck{tc.want, syscall.MS_MGC_VAL}
 | 
						|
					if !reflect.DeepEqual(&got, &want) {
 | 
						|
						t.Errorf("Unmarshal: %#v, want %#v", &got, &want)
 | 
						|
					}
 | 
						|
				}
 | 
						|
			})
 | 
						|
		})
 | 
						|
	}
 | 
						|
 | 
						|
	t.Run("valid", func(t *testing.T) {
 | 
						|
		t.Parallel()
 | 
						|
 | 
						|
		if got := (*hst.FilesystemConfigJSON).Valid(nil); got {
 | 
						|
			t.Errorf("Valid: %v, want false", got)
 | 
						|
		}
 | 
						|
 | 
						|
		if got := new(hst.FilesystemConfigJSON).Valid(); got {
 | 
						|
			t.Errorf("Valid: %v, want false", got)
 | 
						|
		}
 | 
						|
 | 
						|
		if got := (&hst.FilesystemConfigJSON{FilesystemConfig: &hst.FSBind{Source: m("/etc")}}).Valid(); !got {
 | 
						|
			t.Errorf("Valid: %v, want true", got)
 | 
						|
		}
 | 
						|
	})
 | 
						|
 | 
						|
	t.Run("passthrough", func(t *testing.T) {
 | 
						|
		t.Parallel()
 | 
						|
		if err := new(hst.FilesystemConfigJSON).UnmarshalJSON(make([]byte, 0)); err == nil {
 | 
						|
			t.Errorf("UnmarshalJSON: error = %v", err)
 | 
						|
		}
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
func TestFSErrors(t *testing.T) {
 | 
						|
	t.Parallel()
 | 
						|
 | 
						|
	t.Run("type", func(t *testing.T) {
 | 
						|
		t.Parallel()
 | 
						|
		want := `invalid filesystem type "cat"`
 | 
						|
		if got := hst.FSTypeError("cat").Error(); got != want {
 | 
						|
			t.Errorf("Error: %q, want %q", got, want)
 | 
						|
		}
 | 
						|
	})
 | 
						|
 | 
						|
	t.Run("impl", func(t *testing.T) {
 | 
						|
		t.Parallel()
 | 
						|
 | 
						|
		testCases := []struct {
 | 
						|
			name string
 | 
						|
			val  hst.FilesystemConfig
 | 
						|
			want string
 | 
						|
		}{
 | 
						|
			{"nil", nil, "implementation nil not supported"},
 | 
						|
			{"stub", stubFS{"cat"}, "implementation stubFS not supported"},
 | 
						|
			{"*stub", &stubFS{"cat"}, "implementation *stubFS not supported"},
 | 
						|
			{"(*stub)(nil)", (*stubFS)(nil), "implementation *stubFS not supported"},
 | 
						|
		}
 | 
						|
 | 
						|
		for _, tc := range testCases {
 | 
						|
			t.Run(tc.name, func(t *testing.T) {
 | 
						|
				t.Parallel()
 | 
						|
				err := hst.FSImplError{Value: tc.val}
 | 
						|
				if got := err.Error(); got != tc.want {
 | 
						|
					t.Errorf("Error: %q, want %q", got, tc.want)
 | 
						|
				}
 | 
						|
			})
 | 
						|
		}
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
type stubFS struct {
 | 
						|
	typeName string
 | 
						|
}
 | 
						|
 | 
						|
func (s stubFS) Valid() bool             { return false }
 | 
						|
func (s stubFS) Path() *check.Absolute   { panic("unreachable") }
 | 
						|
func (s stubFS) Host() []*check.Absolute { panic("unreachable") }
 | 
						|
func (s stubFS) Apply(*hst.ApplyState)   { panic("unreachable") }
 | 
						|
func (s stubFS) String() string          { return "<invalid " + s.typeName + ">" }
 | 
						|
 | 
						|
type sCheck struct {
 | 
						|
	FS    hst.FilesystemConfigJSON `json:"fs"`
 | 
						|
	Magic int                      `json:"magic"`
 | 
						|
}
 | 
						|
 | 
						|
type fsTestCase struct {
 | 
						|
	name  string
 | 
						|
	fs    hst.FilesystemConfig
 | 
						|
	valid bool
 | 
						|
	ops   container.Ops
 | 
						|
	path  *check.Absolute
 | 
						|
	host  []*check.Absolute
 | 
						|
	str   string
 | 
						|
}
 | 
						|
 | 
						|
func checkFs(t *testing.T, testCases []fsTestCase) {
 | 
						|
	for _, tc := range testCases {
 | 
						|
		t.Run(tc.name, func(t *testing.T) {
 | 
						|
			t.Parallel()
 | 
						|
 | 
						|
			t.Run("valid", func(t *testing.T) {
 | 
						|
				t.Parallel()
 | 
						|
				if got := tc.fs.Valid(); got != tc.valid {
 | 
						|
					t.Errorf("Valid: %v, want %v", got, tc.valid)
 | 
						|
				}
 | 
						|
			})
 | 
						|
 | 
						|
			t.Run("ops", func(t *testing.T) {
 | 
						|
				t.Parallel()
 | 
						|
				ops := new(container.Ops)
 | 
						|
				tc.fs.Apply(&hst.ApplyState{AutoEtcPrefix: ":3", Ops: opsAdapter{ops}})
 | 
						|
				if !reflect.DeepEqual(ops, &tc.ops) {
 | 
						|
					gotString := new(strings.Builder)
 | 
						|
					for _, op := range *ops {
 | 
						|
						gotString.WriteString("\n" + op.String())
 | 
						|
					}
 | 
						|
					wantString := new(strings.Builder)
 | 
						|
					for _, op := range tc.ops {
 | 
						|
						wantString.WriteString("\n" + op.String())
 | 
						|
					}
 | 
						|
					t.Errorf("Apply: %s, want %s", gotString, wantString)
 | 
						|
				}
 | 
						|
			})
 | 
						|
 | 
						|
			t.Run("path", func(t *testing.T) {
 | 
						|
				t.Parallel()
 | 
						|
				if got := tc.fs.Path(); !reflect.DeepEqual(got, tc.path) {
 | 
						|
					t.Errorf("Target: %q, want %q", got, tc.path)
 | 
						|
				}
 | 
						|
			})
 | 
						|
 | 
						|
			t.Run("host", func(t *testing.T) {
 | 
						|
				t.Parallel()
 | 
						|
				if got := tc.fs.Host(); !reflect.DeepEqual(got, tc.host) {
 | 
						|
					t.Errorf("Host: %q, want %q", got, tc.host)
 | 
						|
				}
 | 
						|
			})
 | 
						|
 | 
						|
			t.Run("string", func(t *testing.T) {
 | 
						|
				t.Parallel()
 | 
						|
				if tc.str == "\x00" {
 | 
						|
					return
 | 
						|
				}
 | 
						|
 | 
						|
				if got := tc.fs.String(); got != tc.str {
 | 
						|
					t.Errorf("String: %q, want %q", got, tc.str)
 | 
						|
				}
 | 
						|
			})
 | 
						|
		})
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
type opsAdapter struct{ *container.Ops }
 | 
						|
 | 
						|
func (p opsAdapter) Tmpfs(target *check.Absolute, size int, perm os.FileMode) hst.Ops {
 | 
						|
	return opsAdapter{p.Ops.Tmpfs(target, size, perm)}
 | 
						|
}
 | 
						|
 | 
						|
func (p opsAdapter) Readonly(target *check.Absolute, perm os.FileMode) hst.Ops {
 | 
						|
	return opsAdapter{p.Ops.Readonly(target, perm)}
 | 
						|
}
 | 
						|
 | 
						|
func (p opsAdapter) Bind(source, target *check.Absolute, flags int) hst.Ops {
 | 
						|
	return opsAdapter{p.Ops.Bind(source, target, flags)}
 | 
						|
}
 | 
						|
 | 
						|
func (p opsAdapter) Overlay(target, state, work *check.Absolute, layers ...*check.Absolute) hst.Ops {
 | 
						|
	return opsAdapter{p.Ops.Overlay(target, state, work, layers...)}
 | 
						|
}
 | 
						|
 | 
						|
func (p opsAdapter) OverlayReadonly(target *check.Absolute, layers ...*check.Absolute) hst.Ops {
 | 
						|
	return opsAdapter{p.Ops.OverlayReadonly(target, layers...)}
 | 
						|
}
 | 
						|
 | 
						|
func (p opsAdapter) Link(target *check.Absolute, linkName string, dereference bool) hst.Ops {
 | 
						|
	return opsAdapter{p.Ops.Link(target, linkName, dereference)}
 | 
						|
}
 | 
						|
 | 
						|
func (p opsAdapter) Root(host *check.Absolute, flags int) hst.Ops {
 | 
						|
	return opsAdapter{p.Ops.Root(host, flags)}
 | 
						|
}
 | 
						|
 | 
						|
func (p opsAdapter) Etc(host *check.Absolute, prefix string) hst.Ops {
 | 
						|
	return opsAdapter{p.Ops.Etc(host, prefix)}
 | 
						|
}
 | 
						|
 | 
						|
func m(pathname string) *check.Absolute { return check.MustAbs(pathname) }
 | 
						|
func ms(pathnames ...string) []*check.Absolute {
 | 
						|
	as := make([]*check.Absolute, len(pathnames))
 | 
						|
	for i, pathname := range pathnames {
 | 
						|
		as[i] = check.MustAbs(pathname)
 | 
						|
	}
 | 
						|
	return as
 | 
						|
}
 |