github.com/xyproto/u-root@v6.0.1-0.20200302025726-5528e0c77a3c+incompatible/cmds/core/dd/dd_test.go (about) 1 // Copyright 2017 the u-root Authors. All rights reserved 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package main 6 7 import ( 8 "bufio" 9 "fmt" 10 "io" 11 "io/ioutil" 12 "os" 13 "path/filepath" 14 "reflect" 15 "strings" 16 "testing" 17 18 "github.com/u-root/u-root/pkg/testutil" 19 ) 20 21 // TestDd implements a table-driven test. 22 func TestDd(t *testing.T) { 23 var tests = []struct { 24 name string 25 flags []string 26 stdin string 27 stdout []byte 28 count int64 29 compare func(io.Reader, []byte, int64) error 30 }{ 31 32 { 33 name: "Simple copying from input to output", 34 flags: []string{}, 35 stdin: "1: defaults", 36 stdout: []byte("1: defaults"), 37 compare: stdoutEqual, 38 }, 39 { 40 name: "Copy from input to output on a non-aligned block size", 41 flags: []string{"bs=8c"}, 42 stdin: "2: bs=8c 11b", // len=12 is not multiple of 8 43 stdout: []byte("2: bs=8c 11b"), 44 compare: stdoutEqual, 45 }, 46 { 47 name: "Copy from input to output on an aligned block size", 48 flags: []string{"bs=8"}, 49 stdin: "hello world.....", // len=16 is a multiple of 8 50 stdout: []byte("hello world....."), 51 compare: stdoutEqual, 52 }, 53 { 54 name: "Create a 64KiB zeroed file in 1KiB blocks", 55 flags: []string{"if=/dev/zero", "bs=1K", "count=64"}, 56 stdin: "", 57 stdout: []byte("\x00"), 58 count: 64 * 1024, 59 compare: byteCount, 60 }, 61 { 62 name: "Create a 64KiB zeroed file in 1 byte blocks", 63 flags: []string{"if=/dev/zero", "bs=1", "count=65536"}, 64 stdin: "", 65 stdout: []byte("\x00"), 66 count: 64 * 1024, 67 compare: byteCount, 68 }, 69 { 70 name: "Create a 64KiB zeroed file in one 64KiB block", 71 flags: []string{"if=/dev/zero", "bs=64K", "count=1"}, 72 stdin: "", 73 stdout: []byte("\x00"), 74 count: 64 * 1024, 75 compare: byteCount, 76 }, 77 { 78 name: "Use skip and count", 79 flags: []string{"skip=6", "bs=1", "count=5"}, 80 stdin: "hello world.....", 81 stdout: []byte("world"), 82 compare: stdoutEqual, 83 }, 84 { 85 name: "Count clamps to end of stream", 86 flags: []string{"bs=2", "skip=3", "count=100000"}, 87 stdin: "hello world.....", 88 stdout: []byte("world....."), 89 compare: stdoutEqual, 90 }, 91 { 92 name: "512 MiB zeroed file in 1024 1KiB blocks", 93 flags: []string{"bs=524288", "count=1024", "if=/dev/zero"}, 94 stdin: "", 95 stdout: []byte("\x00"), 96 count: 1024 * 1024 * 512, 97 compare: byteCount, 98 }, 99 } 100 101 for _, tt := range tests { 102 t.Run(tt.name, func(t *testing.T) { 103 cmd := testutil.Command(t, tt.flags...) 104 cmd.Stdin = strings.NewReader(tt.stdin) 105 out, err := cmd.StdoutPipe() 106 if err != nil { 107 t.Fatal(err) 108 } 109 if err := cmd.Start(); err != nil { 110 t.Error(err) 111 } 112 err = tt.compare(out, tt.stdout, tt.count) 113 if err != nil { 114 t.Errorf("Test compare function returned: %v", err) 115 } 116 if err := cmd.Wait(); err != nil { 117 t.Errorf("Test %v exited with error: %v", tt.flags, err) 118 } 119 }) 120 } 121 } 122 123 // stdoutEqual creates a bufio Reader from io.Reader, then compares a byte at a time input []byte. 124 // The third argument (int64) is ignored and only exists to make the function signature compatible 125 // with func byteCount. 126 // Returns an error if mismatch is found with offset. 127 func stdoutEqual(i io.Reader, o []byte, _ int64) error { 128 var count int64 129 b := bufio.NewReader(i) 130 131 for { 132 z, err := b.ReadByte() 133 if err != nil { 134 break 135 } 136 if o[count] != z { 137 return fmt.Errorf("Found mismatch at offset %d, wanted %s, found %s", count, string(o[count]), string(z)) 138 } 139 count++ 140 } 141 return nil 142 } 143 144 // byteCount creates a bufio Reader from io.Reader, then counts the number of sequential bytes 145 // that match the first byte in the input []byte. If the count matches input n int64, nil error 146 // is returned. Otherwise an error is returned for a non-matching byte or if the count doesn't 147 // match. 148 func byteCount(i io.Reader, o []byte, n int64) error { 149 var count int64 150 buf := make([]byte, 4096) 151 152 for { 153 read, err := i.Read(buf) 154 if err != nil || read == 0 { 155 break 156 } 157 for z := 0; z < read; z++ { 158 if buf[z] == o[0] { 159 count++ 160 } else { 161 return fmt.Errorf("Found non-matching byte: %v != %v, at offset: %d", 162 buf[z], o[0], count) 163 } 164 } 165 166 if count > n { 167 break 168 } 169 } 170 171 if count == n { 172 return nil 173 } 174 return fmt.Errorf("Found %d count of %#v bytes, wanted to find %d count", count, o[0], n) 175 } 176 177 // TestFiles uses `if` and `of` arguments. 178 func TestFiles(t *testing.T) { 179 var tests = []struct { 180 name string 181 flags []string 182 inFile []byte 183 outFile []byte 184 expected []byte 185 }{ 186 { 187 name: "Simple copying from input to output", 188 flags: []string{}, 189 inFile: []byte("1: defaults"), 190 expected: []byte("1: defaults"), 191 }, 192 { 193 name: "Copy from input to output on a non-aligned block size", 194 flags: []string{"bs=8c"}, 195 inFile: []byte("2: bs=8c 11b"), // len=12 is not multiple of 8 196 expected: []byte("2: bs=8c 11b"), 197 }, 198 { 199 name: "Copy from input to output on an aligned block size", 200 flags: []string{"bs=8"}, 201 inFile: []byte("hello world....."), // len=16 is a multiple of 8 202 expected: []byte("hello world....."), 203 }, 204 { 205 name: "Use skip and count", 206 flags: []string{"skip=6", "bs=1", "count=5"}, 207 inFile: []byte("hello world....."), 208 expected: []byte("world"), 209 }, 210 { 211 name: "truncate", 212 flags: []string{"bs=1"}, 213 inFile: []byte("1234"), 214 outFile: []byte("abcde"), 215 expected: []byte("1234"), 216 }, 217 { 218 name: "no truncate", 219 flags: []string{"bs=1", "conv=notrunc"}, 220 inFile: []byte("1234"), 221 outFile: []byte("abcde"), 222 expected: []byte("1234e"), 223 }, 224 { 225 // Fully testing the file is synchronous would require something more. 226 name: "sync", 227 flags: []string{"oflag=sync"}, 228 inFile: []byte("x: defaults"), 229 expected: []byte("x: defaults"), 230 }, 231 { 232 // Fully testing the file is synchronous would require something more. 233 name: "dsync", 234 flags: []string{"oflag=dsync"}, 235 inFile: []byte("y: defaults"), 236 expected: []byte("y: defaults"), 237 }, 238 } 239 240 for _, tt := range tests { 241 t.Run(tt.name, func(t *testing.T) { 242 // Write in and out file to temporary dir. 243 tmpDir, err := ioutil.TempDir("", "dd-test") 244 if err != nil { 245 t.Fatal(err) 246 } 247 defer os.RemoveAll(tmpDir) 248 inFile := filepath.Join(tmpDir, "inFile") 249 outFile := filepath.Join(tmpDir, "outFile") 250 if err := ioutil.WriteFile(inFile, tt.inFile, 0666); err != nil { 251 t.Error(err) 252 } 253 if err := ioutil.WriteFile(outFile, tt.outFile, 0666); err != nil { 254 t.Error(err) 255 } 256 257 args := append(tt.flags, "if="+inFile, "of="+outFile) 258 if err := testutil.Command(t, args...).Run(); err != nil { 259 t.Error(err) 260 } 261 got, err := ioutil.ReadFile(filepath.Join(tmpDir, "outFile")) 262 if err != nil { 263 t.Error(err) 264 } 265 if !reflect.DeepEqual(tt.expected, got) { 266 t.Errorf("expected %q, got %q", tt.expected, got) 267 } 268 }) 269 } 270 } 271 272 // BenchmarkDd benchmarks the dd command. Each "op" unit is a 1MiB block. 273 func BenchmarkDd(b *testing.B) { 274 const bytesPerOp = 1024 * 1024 275 b.SetBytes(bytesPerOp) 276 277 args := []string{ 278 "if=/dev/zero", 279 "of=/dev/null", 280 fmt.Sprintf("count=%d", b.N), 281 fmt.Sprintf("bs=%d", bytesPerOp), 282 } 283 b.ResetTimer() 284 if err := testutil.Command(b, args...).Run(); err != nil { 285 b.Fatal(err) 286 } 287 } 288 289 func TestMain(m *testing.M) { 290 testutil.Run(m, main) 291 }