github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/internal/ioutil/ioutil_test.go (about) 1 // Copyright (c) 2015-2023 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package ioutil 19 20 import ( 21 "bytes" 22 "context" 23 "errors" 24 "io" 25 "os" 26 "strings" 27 "testing" 28 "time" 29 ) 30 31 type sleepWriter struct { 32 timeout time.Duration 33 } 34 35 func (w *sleepWriter) Write(p []byte) (n int, err error) { 36 time.Sleep(w.timeout) 37 return len(p), nil 38 } 39 40 func (w *sleepWriter) Close() error { 41 return nil 42 } 43 44 func TestDeadlineWriter(t *testing.T) { 45 w := NewDeadlineWriter(&sleepWriter{timeout: 500 * time.Millisecond}, 450*time.Millisecond) 46 _, err := w.Write([]byte("1")) 47 if err != context.DeadlineExceeded { 48 t.Error("DeadlineWriter shouldn't be successful - should return context.DeadlineExceeded") 49 } 50 _, err = w.Write([]byte("1")) 51 if err != context.DeadlineExceeded { 52 t.Error("DeadlineWriter shouldn't be successful - should return context.DeadlineExceeded") 53 } 54 w.Close() 55 w = NewDeadlineWriter(&sleepWriter{timeout: 100 * time.Millisecond}, 600*time.Millisecond) 56 n, err := w.Write([]byte("abcd")) 57 w.Close() 58 if err != nil { 59 t.Errorf("DeadlineWriter should succeed but failed with %s", err) 60 } 61 if n != 4 { 62 t.Errorf("DeadlineWriter should succeed but should have only written 4 bytes, but returned %d instead", n) 63 } 64 } 65 66 func TestCloseOnWriter(t *testing.T) { 67 writer := WriteOnClose(io.Discard) 68 if writer.HasWritten() { 69 t.Error("WriteOnCloser must not be marked as HasWritten") 70 } 71 writer.Write(nil) 72 if !writer.HasWritten() { 73 t.Error("WriteOnCloser must be marked as HasWritten") 74 } 75 76 writer = WriteOnClose(io.Discard) 77 writer.Close() 78 if !writer.HasWritten() { 79 t.Error("WriteOnCloser must be marked as HasWritten") 80 } 81 } 82 83 // Test for AppendFile. 84 func TestAppendFile(t *testing.T) { 85 f, err := os.CreateTemp("", "") 86 if err != nil { 87 t.Fatal(err) 88 } 89 name1 := f.Name() 90 defer os.Remove(name1) 91 f.WriteString("aaaaaaaaaa") 92 f.Close() 93 94 f, err = os.CreateTemp("", "") 95 if err != nil { 96 t.Fatal(err) 97 } 98 name2 := f.Name() 99 defer os.Remove(name2) 100 f.WriteString("bbbbbbbbbb") 101 f.Close() 102 103 if err = AppendFile(name1, name2, false); err != nil { 104 t.Error(err) 105 } 106 107 b, err := os.ReadFile(name1) 108 if err != nil { 109 t.Error(err) 110 } 111 112 expected := "aaaaaaaaaabbbbbbbbbb" 113 if string(b) != expected { 114 t.Errorf("AppendFile() failed, expected: %s, got %s", expected, string(b)) 115 } 116 } 117 118 func TestSkipReader(t *testing.T) { 119 testCases := []struct { 120 src io.Reader 121 skipLen int64 122 expected string 123 }{ 124 {bytes.NewBuffer([]byte("")), 0, ""}, 125 {bytes.NewBuffer([]byte("")), 1, ""}, 126 {bytes.NewBuffer([]byte("abc")), 0, "abc"}, 127 {bytes.NewBuffer([]byte("abc")), 1, "bc"}, 128 {bytes.NewBuffer([]byte("abc")), 2, "c"}, 129 {bytes.NewBuffer([]byte("abc")), 3, ""}, 130 {bytes.NewBuffer([]byte("abc")), 4, ""}, 131 } 132 for i, testCase := range testCases { 133 r := NewSkipReader(testCase.src, testCase.skipLen) 134 b, err := io.ReadAll(r) 135 if err != nil { 136 t.Errorf("Case %d: Unexpected err %v", i, err) 137 } 138 if string(b) != testCase.expected { 139 t.Errorf("Case %d: Got wrong result: %v", i, string(b)) 140 } 141 } 142 } 143 144 func TestSameFile(t *testing.T) { 145 f, err := os.CreateTemp("", "") 146 if err != nil { 147 t.Errorf("Error creating tmp file: %v", err) 148 } 149 tmpFile := f.Name() 150 f.Close() 151 defer os.Remove(f.Name()) 152 fi1, err := os.Stat(tmpFile) 153 if err != nil { 154 t.Fatalf("Error Stat(): %v", err) 155 } 156 fi2, err := os.Stat(tmpFile) 157 if err != nil { 158 t.Fatalf("Error Stat(): %v", err) 159 } 160 if !SameFile(fi1, fi2) { 161 t.Fatal("Expected the files to be same") 162 } 163 if err = os.WriteFile(tmpFile, []byte("aaa"), 0o644); err != nil { 164 t.Fatal(err) 165 } 166 fi2, err = os.Stat(tmpFile) 167 if err != nil { 168 t.Fatalf("Error Stat(): %v", err) 169 } 170 if SameFile(fi1, fi2) { 171 t.Fatal("Expected the files not to be same") 172 } 173 } 174 175 func TestCopyAligned(t *testing.T) { 176 f, err := os.CreateTemp("", "") 177 if err != nil { 178 t.Errorf("Error creating tmp file: %v", err) 179 } 180 defer f.Close() 181 defer os.Remove(f.Name()) 182 183 r := strings.NewReader("hello world") 184 185 bufp := ODirectPoolSmall.Get().(*[]byte) 186 defer ODirectPoolSmall.Put(bufp) 187 188 written, err := CopyAligned(f, io.LimitReader(r, 5), *bufp, r.Size(), f) 189 if !errors.Is(err, io.ErrUnexpectedEOF) { 190 t.Errorf("Expected io.ErrUnexpectedEOF, but got %v", err) 191 } 192 if written != 5 { 193 t.Errorf("Expected written to be '5', but got %v", written) 194 } 195 196 f.Seek(0, io.SeekStart) 197 r.Seek(0, io.SeekStart) 198 199 written, err = CopyAligned(f, r, *bufp, r.Size(), f) 200 if !errors.Is(err, nil) { 201 t.Errorf("Expected nil, but got %v", err) 202 } 203 if written != r.Size() { 204 t.Errorf("Expected written to be '%v', but got %v", r.Size(), written) 205 } 206 }