File: path_test.go

package info (click to toggle)
golang-github-containers-image 5.36.1-1
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 5,152 kB
  • sloc: sh: 267; makefile: 100
file content (178 lines) | stat: -rw-r--r-- 5,362 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
package explicitfilepath

import (
	"fmt"
	"os"
	"path/filepath"
	"testing"

	_ "github.com/containers/image/v5/internal/testing/explicitfilepath-tmpdir"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

type pathResolvingTestCase struct {
	setup    func(*testing.T, string) string
	expected string
}

var testCases = []pathResolvingTestCase{
	{ // A straightforward subdirectory hierarchy
		func(t *testing.T, top string) string {
			err := os.MkdirAll(filepath.Join(top, "dir1/dir2/dir3"), 0755)
			require.NoError(t, err)
			return "dir1/dir2/dir3"
		},
		"dir1/dir2/dir3",
	},
	{ // Missing component
		func(t *testing.T, top string) string {
			return "thisismissing/dir2"
		},
		"",
	},
	{ // Symlink on the path
		func(t *testing.T, top string) string {
			err := os.MkdirAll(filepath.Join(top, "dir1/dir2"), 0755)
			require.NoError(t, err)
			err = os.Symlink("dir1", filepath.Join(top, "link1"))
			require.NoError(t, err)
			return "link1/dir2"
		},
		"dir1/dir2",
	},
	{ // Trailing symlink
		func(t *testing.T, top string) string {
			err := os.MkdirAll(filepath.Join(top, "dir1/dir2"), 0755)
			require.NoError(t, err)
			err = os.Symlink("dir2", filepath.Join(top, "dir1/link2"))
			require.NoError(t, err)
			return "dir1/link2"
		},
		"dir1/dir2",
	},
	{ // Symlink pointing nowhere, as a non-final component
		func(t *testing.T, top string) string {
			err := os.Symlink("thisismissing", filepath.Join(top, "link1"))
			require.NoError(t, err)
			return "link1/dir2"
		},
		"",
	},
	{ // Trailing symlink pointing nowhere (but note that a missing non-symlink would be accepted)
		func(t *testing.T, top string) string {
			err := os.Symlink("thisismissing", filepath.Join(top, "link1"))
			require.NoError(t, err)
			return "link1"
		},
		"",
	},
	{ // Relative components in a path
		func(t *testing.T, top string) string {
			err := os.MkdirAll(filepath.Join(top, "dir1/dir2/dir3"), 0755)
			require.NoError(t, err)
			return "dir1/./dir2/../dir2/dir3"
		},
		"dir1/dir2/dir3",
	},
	{ // Trailing relative components
		func(t *testing.T, top string) string {
			err := os.MkdirAll(filepath.Join(top, "dir1/dir2"), 0755)
			require.NoError(t, err)
			return "dir1/dir2/.."
		},
		"dir1",
	},
	{ // Relative components in symlink
		func(t *testing.T, top string) string {
			err := os.MkdirAll(filepath.Join(top, "dir1/dir2"), 0755)
			require.NoError(t, err)
			err = os.Symlink("../dir1/dir2", filepath.Join(top, "dir1/link2"))
			require.NoError(t, err)
			return "dir1/link2"
		},
		"dir1/dir2",
	},
	{ // Relative component pointing "into" a symlink
		func(t *testing.T, top string) string {
			err := os.MkdirAll(filepath.Join(top, "dir1/dir2/dir3"), 0755)
			require.NoError(t, err)
			err = os.Symlink("dir3", filepath.Join(top, "dir1/dir2/link3"))
			require.NoError(t, err)
			return "dir1/dir2/link3/../.."
		},
		"dir1",
	},
	{ // Unreadable directory
		func(t *testing.T, top string) string {
			err := os.MkdirAll(filepath.Join(top, "unreadable/dir2"), 0755)
			require.NoError(t, err)
			err = os.Chmod(filepath.Join(top, "unreadable"), 000)
			require.NoError(t, err)
			return "unreadable/dir2"
		},
		"",
	},
}

func testPathsAreSameFile(t *testing.T, path1, path2, description string) {
	fi1, err := os.Stat(path1)
	require.NoError(t, err)
	fi2, err := os.Stat(path2)
	require.NoError(t, err)
	assert.True(t, os.SameFile(fi1, fi2), description)
}

func runPathResolvingTestCase(t *testing.T, f func(string) (string, error), c pathResolvingTestCase, suffix string) {
	topDir := t.TempDir()
	defer func() {
		// Clean up after the "Unreadable directory" case; os.RemoveAll just fails without this.
		_ = os.Chmod(filepath.Join(topDir, "unreadable"), 0755) // Ignore errors, especially if this does not exist.
	}()

	input := c.setup(t, topDir) + suffix // Do not call filepath.Join() on input, it calls filepath.Clean() internally!
	description := fmt.Sprintf("%s vs. %s%s", input, c.expected, suffix)

	fullOutput, err := f(topDir + "/" + input)
	if c.expected == "" {
		assert.Error(t, err, description)
	} else {
		require.NoError(t, err, input)
		fullExpected := topDir + "/" + c.expected + suffix
		assert.Equal(t, fullExpected, fullOutput)

		// Either the two paths resolve to the same existing file, or to the same name in the same existing parent.
		if _, err := os.Lstat(fullExpected); err == nil {
			testPathsAreSameFile(t, fullOutput, fullExpected, description)
		} else {
			require.True(t, os.IsNotExist(err))
			_, err := os.Stat(fullOutput)
			require.Error(t, err)
			require.True(t, os.IsNotExist(err))

			parentExpected, fileExpected := filepath.Split(fullExpected)
			parentOutput, fileOutput := filepath.Split(fullOutput)
			assert.Equal(t, fileExpected, fileOutput)
			testPathsAreSameFile(t, parentOutput, parentExpected, description)
		}
	}
}

func TestResolvePathToFullyExplicit(t *testing.T) {
	if os.Geteuid() == 0 {
		t.Skip("Test must not run as root")
	}
	for _, c := range testCases {
		runPathResolvingTestCase(t, ResolvePathToFullyExplicit, c, "")
		runPathResolvingTestCase(t, ResolvePathToFullyExplicit, c, "/trailing")
	}
}

func TestResolveExistingPathToFullyExplicit(t *testing.T) {
	if os.Geteuid() == 0 {
		t.Skip("Test must not run as root")
	}
	for _, c := range testCases {
		runPathResolvingTestCase(t, resolveExistingPathToFullyExplicit, c, "")
	}
}