Welcome to mirror list, hosted at ThFree Co, Russian Federation.

sendfile_test.go « streamcache « internal - gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 769198cf099fd72d146d7c089e106941adf055bd (plain)
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
//go:build linux
// +build linux

package streamcache

import (
	"bytes"
	"io"
	"math/rand"
	"os"
	"path/filepath"
	"testing"
	"testing/iotest"

	"github.com/stretchr/testify/require"
	"gitlab.com/gitlab-org/gitaly/v14/internal/testhelper"
)

type wrappedFile struct{ f *os.File }

func (wf *wrappedFile) Write(p []byte) (int, error) { return wf.f.Write(p) }
func (wf *wrappedFile) Close() error                { return wf.f.Close() }
func (wf *wrappedFile) Name() string                { return wf.f.Name() }

func TestPipe_WriteTo(t *testing.T) {
	data := make([]byte, 10*1024*1024)
	_, err := rand.Read(data)
	require.NoError(t, err)

	testCases := []struct {
		desc     string
		create   func(t *testing.T) namedWriteCloser
		sendfile bool
	}{
		{
			desc: "os.File",
			create: func(t *testing.T) namedWriteCloser {
				f, err := os.Create(filepath.Join(testhelper.TempDir(t), "pipe write to"))
				require.NoError(t, err)
				t.Cleanup(func() { require.NoError(t, os.Remove(f.Name())) })
				return f
			},
			sendfile: true,
		},
		{
			desc: "non-file writer",
			create: func(t *testing.T) namedWriteCloser {
				f, err := os.Create(filepath.Join(testhelper.TempDir(t), "pipe write to"))
				require.NoError(t, err)
				t.Cleanup(func() { require.NoError(t, os.Remove(f.Name())) })
				return &wrappedFile{f}
			},
		},
	}

	for _, tc := range testCases {
		t.Run(tc.desc, func(t *testing.T) {
			pr, p := createPipe(t)
			defer pr.Close()

			errC := make(chan error, 1)
			go func() {
				errC <- func() error {
					defer p.Close()

					// To exercise pipe blocking logic, we want to prevent writing all of
					// data at once.
					r := iotest.HalfReader(bytes.NewReader(data))
					if _, err := io.Copy(p, r); err != nil {
						return err
					}
					return p.Close()
				}()
			}()

			outW := tc.create(t)
			require.NoError(t, err)
			defer outW.Close()

			n, err := pr.WriteTo(outW)
			require.NoError(t, err)
			require.Equal(t, int64(len(data)), n)

			require.NoError(t, outW.Close())
			require.NoError(t, <-errC)

			outBytes, err := os.ReadFile(outW.Name())
			require.NoError(t, err)
			// Don't use require.Equal because we don't want a 10MB error message.
			require.True(t, bytes.Equal(data, outBytes))

			require.Equal(t, tc.sendfile, pr.sendfileCalledSuccessfully)
		})
	}
}

func TestPipe_WriteTo_EAGAIN(t *testing.T) {
	data := make([]byte, 10*1024*1024)
	_, err := rand.Read(data)
	require.NoError(t, err)

	pr, p := createPipe(t)
	defer pr.Close()
	defer p.Close()

	_, err = p.Write(data)
	require.NoError(t, err)
	require.NoError(t, p.Close())

	fr, fw, err := os.Pipe()
	require.NoError(t, err)
	defer fr.Close()
	defer fw.Close()

	errC := make(chan error, 1)
	go func() {
		errC <- func() error {
			defer fw.Close()

			// This will try to write 10MB into fw at once, which will fail because
			// the pipe buffer is too small. Then sendfile will return EAGAIN. Doing
			// this tests our ability to handle EAGAIN correctly.
			_, err := pr.WriteTo(fw)
			if err != nil {
				return err
			}

			return fw.Close()
		}()
	}()

	out, err := io.ReadAll(fr)
	require.NoError(t, err)
	// Don't use require.Equal because we don't want a 10MB error message.
	require.True(t, bytes.Equal(data, out))

	require.NoError(t, <-errC)
	require.True(t, pr.sendfileCalledSuccessfully)
}