| // Copyright 2025 The Dawn & Tint Authors | 
 | // | 
 | // Redistribution and use in source and binary forms, with or without | 
 | // modification, are permitted provided that the following conditions are met: | 
 | // | 
 | // 1. Redistributions of source code must retain the above copyright notice, this | 
 | //    list of conditions and the following disclaimer. | 
 | // | 
 | // 2. Redistributions in binary form must reproduce the above copyright notice, | 
 | //    this list of conditions and the following disclaimer in the documentation | 
 | //    and/or other materials provided with the distribution. | 
 | // | 
 | // 3. Neither the name of the copyright holder nor the names of its | 
 | //    contributors may be used to endorse or promote products derived from | 
 | //    this software without specific prior written permission. | 
 | // | 
 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | 
 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | 
 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | 
 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 
 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | 
 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | 
 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | 
 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
 |  | 
 | package oswrapper | 
 |  | 
 | import ( | 
 | 	"io" | 
 | 	"io/fs" | 
 | 	"os" | 
 | 	"regexp" | 
 | 	"testing" | 
 | 	"time" | 
 |  | 
 | 	"github.com/stretchr/testify/require" | 
 | ) | 
 |  | 
 | func TestUsableAsOsWrapper(t *testing.T) { | 
 | 	f := func(osWrapper OSWrapper) { | 
 | 		_ = osWrapper.Environ() | 
 | 	} | 
 |  | 
 | 	wrapper := CreateMemMapOSWrapper() | 
 | 	f(wrapper) | 
 | } | 
 |  | 
 | func TestEnviron(t *testing.T) { | 
 | 	wrapper := CreateMemMapOSWrapper() | 
 | 	environ := wrapper.Environ() | 
 | 	require.Equal(t, []string{}, environ) | 
 |  | 
 | 	wrapper.MemMapEnvironProvider.Environment = map[string]string{ | 
 | 		"HOME": "/tmp", | 
 | 		"PWD":  "/local", | 
 | 	} | 
 | 	environ = wrapper.Environ() | 
 | 	require.Equal(t, []string{"HOME=/tmp", "PWD=/local"}, environ) | 
 | } | 
 |  | 
 | func TestGetenv(t *testing.T) { | 
 | 	wrapper := CreateMemMapOSWrapper() | 
 | 	require.Equal(t, "", wrapper.Getenv("HOME")) | 
 |  | 
 | 	wrapper.MemMapEnvironProvider.Environment = map[string]string{ | 
 | 		"HOME": "/tmp", | 
 | 	} | 
 | 	require.Equal(t, "/tmp", wrapper.Getenv("HOME")) | 
 | } | 
 |  | 
 | func TestGetwd(t *testing.T) { | 
 | 	wrapper := CreateMemMapOSWrapper() | 
 | 	wd, err := wrapper.Getwd() | 
 | 	require.NoErrorf(t, err, "Failed to get wd: %v", err) | 
 | 	require.Equal(t, "/", wd) | 
 |  | 
 | 	wrapper.MemMapEnvironProvider.Environment = map[string]string{ | 
 | 		"PWD": "/local", | 
 | 	} | 
 | 	wd, err = wrapper.Getwd() | 
 | 	require.NoErrorf(t, err, "Failed to get wd: %v", err) | 
 | 	require.Equal(t, "/local", wd) | 
 | } | 
 |  | 
 | func TestUserHomeDir(t *testing.T) { | 
 | 	tests := []struct { | 
 | 		name        string | 
 | 		environment map[string]string | 
 | 		want        string | 
 | 	}{ | 
 | 		{ | 
 | 			name:        "Default", | 
 | 			environment: map[string]string{}, | 
 | 			want:        "/", | 
 | 		}, | 
 | 		{ | 
 | 			name: "POSIX", | 
 | 			environment: map[string]string{ | 
 | 				"HOME":        "/posix", | 
 | 				"USERPROFILE": "/windows", | 
 | 			}, | 
 | 			want: "/posix", | 
 | 		}, | 
 | 		{ | 
 | 			name: "Windows", | 
 | 			environment: map[string]string{ | 
 | 				"USERPROFILE": "/windows", | 
 | 			}, | 
 | 			want: "/windows", | 
 | 		}, | 
 | 	} | 
 | 	for _, testCase := range tests { | 
 | 		t.Run(testCase.name, func(t *testing.T) { | 
 | 			wrapper := CreateMemMapOSWrapper() | 
 | 			wrapper.MemMapEnvironProvider.Environment = testCase.environment | 
 | 			home, err := wrapper.UserHomeDir() | 
 | 			require.NoErrorf(t, err, "Failed to get home dir: %v", err) | 
 | 			require.Equal(t, testCase.want, home) | 
 | 		}) | 
 | 	} | 
 | } | 
 |  | 
 | func TestOpen_Nonexistent(t *testing.T) { | 
 | 	wrapper := CreateMemMapOSWrapper() | 
 | 	_, err := wrapper.Open("/foo.txt") | 
 | 	require.ErrorContains(t, err, "open /foo.txt: file does not exist") | 
 | } | 
 |  | 
 | func TestOpenFile_Nonexistent(t *testing.T) { | 
 | 	wrapper := CreateMemMapOSWrapper() | 
 | 	_, err := wrapper.OpenFile("/foo.txt", os.O_RDONLY, 0o700) | 
 | 	require.ErrorContains(t, err, "open /foo.txt: file does not exist") | 
 | } | 
 |  | 
 | func TestReadFile_Nonexistent(t *testing.T) { | 
 | 	wrapper := CreateMemMapOSWrapper() | 
 | 	_, err := wrapper.ReadFile("/foo.txt") | 
 | 	require.ErrorContains(t, err, "open /foo.txt: file does not exist") | 
 | } | 
 |  | 
 | func TestStat_Nonexistent(t *testing.T) { | 
 | 	wrapper := CreateMemMapOSWrapper() | 
 | 	_, err := wrapper.Stat("/foo.txt") | 
 | 	require.ErrorContains(t, err, "open /foo.txt: file does not exist") | 
 | } | 
 |  | 
 | func TestWalk_Nonexistent(t *testing.T) { | 
 | 	wrapper := CreateMemMapOSWrapper() | 
 |  | 
 | 	walkfunc := func(root string, info fs.FileInfo, err error) error { | 
 | 		require.Equal(t, "/nonexistent", root) | 
 | 		require.Equal(t, nil, info) | 
 | 		require.ErrorContains(t, err, "open /nonexistent: file does not exist") | 
 | 		return err | 
 | 	} | 
 |  | 
 | 	err := wrapper.Walk("/nonexistent", walkfunc) | 
 | 	require.ErrorContains(t, err, "open /nonexistent: file does not exist") | 
 | } | 
 |  | 
 | func TestWriteFile_Open(t *testing.T) { | 
 | 	wrapper := CreateMemMapOSWrapper() | 
 | 	err := wrapper.WriteFile("/foo.txt", []byte("content"), 0o700) | 
 | 	require.NoErrorf(t, err, "Failed to write file: %v", err) | 
 |  | 
 | 	file, err := wrapper.Open("/foo.txt") | 
 | 	require.NoErrorf(t, err, "Failed to open file: %v", err) | 
 | 	buffer := make([]byte, 7) | 
 | 	bytesRead, err := file.Read(buffer) | 
 | 	require.NoErrorf(t, err, "Failed to read from file: %v", err) | 
 | 	require.Equal(t, []byte("content"), buffer) | 
 | 	require.Equal(t, 7, bytesRead) | 
 | 	bytesRead, err = file.Read(buffer) | 
 | 	require.Error(t, io.EOF, "Did not get EOF") | 
 | 	require.Equal(t, 0, bytesRead) | 
 | } | 
 |  | 
 | func TestCreate_Walk_NonexistentDirectory(t *testing.T) { | 
 | 	wrapper := CreateMemMapOSWrapper() | 
 | 	_, err := wrapper.Create("/parent/foo.txt") | 
 | 	require.NoErrorf(t, err, "Got error creating file without parent dir: %v", err) | 
 |  | 
 | 	type input struct { | 
 | 		root string | 
 | 		info fs.FileInfo | 
 | 		err  error | 
 | 	} | 
 | 	inputs := []input{} | 
 |  | 
 | 	walkfunc := func(root string, info fs.FileInfo, err error) error { | 
 | 		inputs = append(inputs, input{root: root, info: info, err: err}) | 
 | 		return err | 
 | 	} | 
 |  | 
 | 	err = wrapper.Walk("/parent", walkfunc) | 
 | 	require.NoErrorf(t, err, "Got error walking: %v", err) | 
 | 	require.Equal(t, 2, len(inputs)) | 
 | 	require.Equal(t, "/parent", inputs[0].root) | 
 | 	require.True(t, inputs[0].info.IsDir()) | 
 | 	require.Equal(t, "/parent/foo.txt", inputs[1].root) | 
 | 	require.False(t, inputs[1].info.IsDir()) | 
 | } | 
 |  | 
 | func TestCreate_ReadFile(t *testing.T) { | 
 | 	wrapper := CreateMemMapOSWrapper() | 
 | 	file, err := wrapper.Create("/foo.txt") | 
 | 	require.NoErrorf(t, err, "Failed to create file: %v", err) | 
 |  | 
 | 	bytesWritten, err := file.Write([]byte("asdf")) | 
 | 	require.NoErrorf(t, err, "Failed to write to file: %v", err) | 
 | 	require.Equal(t, 4, bytesWritten) | 
 |  | 
 | 	contents, err := wrapper.ReadFile("/foo.txt") | 
 | 	require.NoErrorf(t, err, "Failed to read file: %v", err) | 
 | 	require.Equal(t, []byte("asdf"), contents) | 
 | } | 
 |  | 
 | func TestMkdir_Exists(t *testing.T) { | 
 | 	wrapper := CreateMemMapOSWrapper() | 
 | 	err := wrapper.Mkdir("/parent", 0o700) | 
 | 	require.NoErrorf(t, err, "Got error creating directory: %v", err) | 
 | 	err = wrapper.Mkdir("/parent", 0o700) | 
 | 	require.ErrorContains(t, err, "mkdir /parent: file already exists") | 
 | } | 
 |  | 
 | func TestMkdir_MkdirAll(t *testing.T) { | 
 | 	wrapper := CreateMemMapOSWrapper() | 
 | 	err := wrapper.Mkdir("/parent", 0o700) | 
 | 	require.NoErrorf(t, err, "Error creating parent: %v", err) | 
 |  | 
 | 	err = wrapper.MkdirAll("/parent/child/grandchild", 0o600) | 
 | 	require.NoErrorf(t, err, "Error creating grandchild: %v", err) | 
 |  | 
 | 	type input struct { | 
 | 		root string | 
 | 		info fs.FileInfo | 
 | 		err  error | 
 | 	} | 
 | 	inputs := []input{} | 
 |  | 
 | 	walkfunc := func(root string, info fs.FileInfo, err error) error { | 
 | 		inputs = append(inputs, input{root: root, info: info, err: err}) | 
 | 		return err | 
 | 	} | 
 |  | 
 | 	err = wrapper.Walk("/parent", walkfunc) | 
 | 	require.NoErrorf(t, err, "Got error walking: %v", err) | 
 | 	require.Equal(t, len(inputs), 3) | 
 |  | 
 | 	require.Equal(t, "/parent", inputs[0].root) | 
 | 	require.True(t, inputs[0].info.IsDir()) | 
 | 	require.Equal(t, os.FileMode(0o700)|fs.ModeDir, inputs[0].info.Mode()) | 
 |  | 
 | 	require.Equal(t, "/parent/child", inputs[1].root) | 
 | 	require.True(t, inputs[1].info.IsDir()) | 
 | 	require.Equal(t, os.FileMode(0o600)|fs.ModeDir, inputs[1].info.Mode()) | 
 |  | 
 | 	require.Equal(t, "/parent/child/grandchild", inputs[2].root) | 
 | 	require.True(t, inputs[2].info.IsDir()) | 
 | 	require.Equal(t, os.FileMode(0o600)|fs.ModeDir, inputs[2].info.Mode()) | 
 | } | 
 |  | 
 | func TestMkdirAll_Exists(t *testing.T) { | 
 | 	wrapper := CreateMemMapOSWrapper() | 
 | 	err := wrapper.MkdirAll("/parent", 0o700) | 
 | 	require.NoErrorf(t, err, "Got error creating directory: %v", err) | 
 | 	err = wrapper.MkdirAll("/parent", 0o700) | 
 | 	require.NoErrorf(t, err, "Got error creating directory second time: %v", err) | 
 | } | 
 |  | 
 | func TestMkdirTemp(t *testing.T) { | 
 | 	tests := []struct { | 
 | 		name    string | 
 | 		pattern string | 
 | 		re      string | 
 | 	}{ | 
 | 		{ | 
 | 			name:    "Default behavior", | 
 | 			pattern: "tempdir", | 
 | 			re:      "/tmp/tempdir\\d+", | 
 | 		}, | 
 | 		{ | 
 | 			name:    "Single star front", | 
 | 			pattern: "*tempdir", | 
 | 			re:      "/tmp/\\d+tempdir", | 
 | 		}, | 
 | 		{ | 
 | 			name:    "Single star middle", | 
 | 			pattern: "temp*dir", | 
 | 			re:      "/tmp/temp\\d+dir", | 
 | 		}, | 
 | 		{ | 
 | 			name:    "Multiple stars", | 
 | 			pattern: "temp*dir*", | 
 | 			re:      "/tmp/temp\\*dir\\d+", | 
 | 		}, | 
 | 	} | 
 |  | 
 | 	for _, testCase := range tests { | 
 | 		t.Run(testCase.name, func(t *testing.T) { | 
 | 			wrapper := CreateMemMapOSWrapper() | 
 | 			dir, err := wrapper.MkdirTemp("/tmp", testCase.pattern) | 
 | 			require.NoErrorf(t, err, "Error creating temporary directory: %v", err) | 
 |  | 
 | 			re, err := regexp.Compile(testCase.re) | 
 | 			require.NoErrorf(t, err, "Error compiling regexp: %s", err) | 
 | 			require.True(t, re.Match([]byte(dir))) | 
 | 		}) | 
 | 	} | 
 | } | 
 |  | 
 | func TestRemove(t *testing.T) { | 
 | 	tests := []struct { | 
 | 		name          string | 
 | 		filesToCreate []string | 
 | 		dirsToCreate  []string | 
 | 		toRemove      string | 
 | 		wantErr       bool | 
 | 		wantErrMsg    string | 
 | 	}{ | 
 | 		{ | 
 | 			name:       "Non-existent", | 
 | 			toRemove:   "/foo.txt", | 
 | 			wantErr:    true, | 
 | 			wantErrMsg: "remove /foo.txt: file does not exist", | 
 | 		}, | 
 | 		{ | 
 | 			name:          "File", | 
 | 			filesToCreate: []string{"/foo.txt"}, | 
 | 			toRemove:      "/foo.txt", | 
 | 		}, | 
 | 		{ | 
 | 			name:         "Empty dir", | 
 | 			dirsToCreate: []string{"/foo"}, | 
 | 			toRemove:     "/foo", | 
 | 		}, | 
 | 		{ | 
 | 			// This differs from the real os implementation, as removing a non-empty | 
 | 			// dir is supposed to fail. | 
 | 			name:         "Non-empty dir", | 
 | 			dirsToCreate: []string{"/foo/bar"}, | 
 | 			toRemove:     "/foo", | 
 | 		}, | 
 | 	} | 
 |  | 
 | 	for _, testCase := range tests { | 
 | 		t.Run(testCase.name, func(t *testing.T) { | 
 | 			wrapper := CreateMemMapOSWrapper() | 
 | 			for _, f := range testCase.filesToCreate { | 
 | 				_, err := wrapper.Create(f) | 
 | 				require.NoErrorf(t, err, "Failed to create file: %v", err) | 
 | 			} | 
 | 			for _, d := range testCase.dirsToCreate { | 
 | 				err := wrapper.MkdirAll(d, 0o700) | 
 | 				require.NoErrorf(t, err, "Failed to create directory: %v", err) | 
 | 			} | 
 | 			err := wrapper.Remove(testCase.toRemove) | 
 | 			if testCase.wantErr { | 
 | 				require.ErrorContains(t, err, testCase.wantErrMsg) | 
 | 			} else { | 
 | 				require.NoErrorf(t, err, "Got unexpected error: %v") | 
 | 			} | 
 |  | 
 | 			// Make sure things were actually deleted. | 
 | 			walkfunc := func(root string, info fs.FileInfo, err error) error { | 
 | 				require.Equal(t, "/", root) | 
 | 				return err | 
 | 			} | 
 |  | 
 | 			err = wrapper.Walk("/", walkfunc) | 
 | 			require.NoErrorf(t, err, "Error walking: %v", err) | 
 | 		}) | 
 | 	} | 
 | } | 
 |  | 
 | func TestRemoveAll(t *testing.T) { | 
 | 	tests := []struct { | 
 | 		name          string | 
 | 		filesToCreate []string | 
 | 		dirsToCreate  []string | 
 | 		toRemove      string | 
 | 	}{ | 
 | 		{ | 
 | 			name:     "Non-existent", | 
 | 			toRemove: "/foo.txt", | 
 | 		}, | 
 | 		{ | 
 | 			name:          "File", | 
 | 			filesToCreate: []string{"/foo.txt"}, | 
 | 			toRemove:      "/foo.txt", | 
 | 		}, | 
 | 		{ | 
 | 			name:         "Empty dir", | 
 | 			dirsToCreate: []string{"/foo"}, | 
 | 			toRemove:     "/foo", | 
 | 		}, | 
 | 		{ | 
 | 			// This differs from the real os implementation, as removing a non-empty | 
 | 			// dir is supposed to fail. | 
 | 			name:         "Non-empty dir", | 
 | 			dirsToCreate: []string{"/foo/bar"}, | 
 | 			toRemove:     "/foo", | 
 | 		}, | 
 | 	} | 
 |  | 
 | 	for _, testCase := range tests { | 
 | 		t.Run(testCase.name, func(t *testing.T) { | 
 | 			wrapper := CreateMemMapOSWrapper() | 
 | 			for _, f := range testCase.filesToCreate { | 
 | 				_, err := wrapper.Create(f) | 
 | 				require.NoErrorf(t, err, "Failed to create file: %v", err) | 
 | 			} | 
 | 			for _, d := range testCase.dirsToCreate { | 
 | 				err := wrapper.MkdirAll(d, 0o700) | 
 | 				require.NoErrorf(t, err, "Failed to create directory: %v", err) | 
 | 			} | 
 | 			err := wrapper.RemoveAll(testCase.toRemove) | 
 | 			require.NoErrorf(t, err, "Got unexpected error: %v") | 
 |  | 
 | 			// Make sure things were actually deleted. | 
 | 			walkfunc := func(root string, info fs.FileInfo, err error) error { | 
 | 				require.Equal(t, "/", root) | 
 | 				return err | 
 | 			} | 
 |  | 
 | 			err = wrapper.Walk("/", walkfunc) | 
 | 			require.NoErrorf(t, err, "Error walking: %v", err) | 
 | 		}) | 
 | 	} | 
 | } | 
 |  | 
 | func TestWriteFile_OpenFile(t *testing.T) { | 
 | 	wrapper := CreateMemMapOSWrapper() | 
 | 	err := wrapper.WriteFile("/foo.txt", []byte("content"), 0o700) | 
 | 	require.NoErrorf(t, err, "Failed to write file: %v", err) | 
 |  | 
 | 	file, err := wrapper.OpenFile("/foo.txt", os.O_RDONLY, 0o700) | 
 | 	require.NoErrorf(t, err, "Failed to open file: %v", err) | 
 | 	buffer := make([]byte, 7) | 
 | 	bytesRead, err := file.Read(buffer) | 
 | 	require.NoErrorf(t, err, "Failed to read from file: %v", err) | 
 | 	require.Equal(t, []byte("content"), buffer) | 
 | 	require.Equal(t, 7, bytesRead) | 
 | 	bytesRead, err = file.Read(buffer) | 
 | 	require.Errorf(t, io.EOF, "Did not get EOF: %v", err) | 
 | 	require.Equal(t, 0, bytesRead) | 
 | } | 
 |  | 
 | func TestWriteFile_ReadFile(t *testing.T) { | 
 | 	wrapper := CreateMemMapOSWrapper() | 
 | 	err := wrapper.WriteFile("/foo.txt", []byte("content"), 0o700) | 
 | 	require.NoErrorf(t, err, "Failed to write file: %v", err) | 
 |  | 
 | 	contents, err := wrapper.ReadFile("/foo.txt") | 
 | 	require.NoErrorf(t, err, "Failed to read file: %v", err) | 
 | 	require.Equal(t, []byte("content"), contents) | 
 | } | 
 |  | 
 | func TestWriteFile_Stat(t *testing.T) { | 
 | 	wrapper := CreateMemMapOSWrapper() | 
 | 	approxWriteTime := time.Now() | 
 | 	err := wrapper.WriteFile("/foo.txt", []byte("content"), 0o700) | 
 | 	require.NoErrorf(t, err, "Failed to write file: %v", err) | 
 |  | 
 | 	fileInfo, err := wrapper.Stat("/foo.txt") | 
 | 	require.NoErrorf(t, err, "Got error stat-ing file: %v", err) | 
 | 	require.Equal(t, "foo.txt", fileInfo.Name()) | 
 | 	require.Equal(t, int64(7), fileInfo.Size()) | 
 | 	require.Equal(t, os.FileMode(0o700), fileInfo.Mode()) | 
 | 	// Assert that the actual mod time is within 10ms of when we requested the | 
 | 	// write. | 
 | 	require.LessOrEqual(t, fileInfo.ModTime().UnixMilli()-approxWriteTime.UnixMilli(), int64(10)) | 
 | 	require.False(t, fileInfo.IsDir()) | 
 | } |