github.com/rck/u-root@v0.0.0-20180106144920-7eb602e381bb/cmds/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 "os" 12 "os/exec" 13 "strings" 14 "testing" 15 16 "github.com/u-root/u-root/pkg/testutil" 17 ) 18 19 // TestDd implements a table-driven test. 20 func TestDd(t *testing.T) { 21 var tests = []struct { 22 name string 23 flags []string 24 stdin string 25 stdout []byte 26 count int64 27 compare func(io.Reader, []byte, int64) error 28 }{ 29 30 { 31 name: "Simple copying from input to output", 32 flags: []string{}, 33 stdin: "1: defaults", 34 stdout: []byte("1: defaults"), 35 compare: stdoutEqual, 36 }, 37 { 38 name: "Copy from input to output on a non-aligned block size", 39 flags: []string{"bs=8c"}, 40 stdin: "2: bs=8c 11b", // len=12 is not multiple of 8 41 stdout: []byte("2: bs=8c 11b"), 42 compare: stdoutEqual, 43 }, 44 { 45 name: "case lower change", 46 flags: []string{"bs=8", "conv=lcase"}, 47 stdin: "3: Bs=8 11B", // len=11 is not multiple of 8 48 stdout: []byte("3: bs=8 11b"), 49 compare: stdoutEqual, 50 }, 51 { 52 name: "case upper change", 53 flags: []string{"bs=8", "conv=ucase"}, 54 stdin: "3: Bs=8 11B", // len=11 is not multiple of 8 55 stdout: []byte("3: BS=8 11B"), 56 compare: stdoutEqual, 57 }, 58 { 59 name: "Copy from input to output on an aligned block size", 60 flags: []string{"bs=8"}, 61 stdin: "hello world.....", // len=16 is a multiple of 8 62 stdout: []byte("hello world....."), 63 compare: stdoutEqual, 64 }, 65 { 66 name: "Create a 64KiB zeroed file in 1KiB blocks", 67 flags: []string{"if=/dev/zero", "bs=1K", "count=64"}, 68 stdin: "", 69 stdout: []byte("\x00"), 70 count: 64 * 1024, 71 compare: byteCount, 72 }, 73 { 74 name: "Create a 64KiB zeroed file in 1 byte blocks", 75 flags: []string{"if=/dev/zero", "bs=1", "count=65536"}, 76 stdin: "", 77 stdout: []byte("\x00"), 78 count: 64 * 1024, 79 compare: byteCount, 80 }, 81 { 82 name: "Create a 64KiB zeroed file in one 64KiB block", 83 flags: []string{"if=/dev/zero", "bs=64K", "count=1"}, 84 stdin: "", 85 stdout: []byte("\x00"), 86 count: 64 * 1024, 87 compare: byteCount, 88 }, 89 { 90 name: "Use skip and count", 91 flags: []string{"skip=6", "bs=1", "count=5"}, 92 stdin: "hello world.....", 93 stdout: []byte("world"), 94 compare: stdoutEqual, 95 }, 96 { 97 name: "Count clamps to end of stream", 98 flags: []string{"bs=2", "skip=3", "count=100000"}, 99 stdin: "hello world.....", 100 stdout: []byte("world....."), 101 compare: stdoutEqual, 102 }, 103 { 104 name: "1 GiB zeroed file in 1024 1KiB blocks", 105 flags: []string{"bs=1048576", "count=1024", "if=/dev/zero"}, 106 stdin: "", 107 stdout: []byte("\x00"), 108 count: 1024 * 1024 * 1024, 109 compare: byteCount, 110 }, 111 } 112 tmpDir, execPath := testutil.CompileInTempDir(t) 113 defer os.RemoveAll(tmpDir) 114 115 for _, tt := range tests { 116 t.Run(tt.name, func(t *testing.T) { 117 cmd := exec.Command(execPath, tt.flags...) 118 cmd.Stdin = strings.NewReader(tt.stdin) 119 out, err := cmd.StdoutPipe() 120 if err != nil { 121 t.Errorf("Test %v exited with error: %v", tt.flags, err) 122 } 123 if err := cmd.Start(); err != nil { 124 t.Errorf("Test %v exited with error: %v", tt.flags, err) 125 } 126 err = tt.compare(out, tt.stdout, tt.count) 127 if err != nil { 128 t.Errorf("Test compare function returned: %v", err) 129 } 130 if err := cmd.Wait(); err != nil { 131 t.Errorf("Test %v exited with error: %v", tt.flags, err) 132 } 133 }) 134 } 135 } 136 137 // stdoutEqual creates a bufio Reader from io.Reader, then compares a byte at a time input []byte. 138 // The third argument (int64) is ignored and only exists to make the function signature compatible 139 // with func byteCount. 140 // Returns an error if mismatch is found with offset. 141 func stdoutEqual(i io.Reader, o []byte, _ int64) error { 142 var count int64 143 b := bufio.NewReader(i) 144 145 for { 146 z, err := b.ReadByte() 147 if err != nil { 148 break 149 } 150 if o[count] != z { 151 return fmt.Errorf("Found mismatch at offset %d, wanted %s, found %s", count, string(o[count]), string(z)) 152 } 153 count++ 154 } 155 return nil 156 } 157 158 // byteCount creates a bufio Reader from io.Reader, then counts the number of sequential bytes 159 // that match the first byte in the input []byte. If the count matches input n int64, nil error 160 // is returned. Otherwise an error is returned for a non-matching byte or if the count doesn't 161 // match. 162 func byteCount(i io.Reader, o []byte, n int64) error { 163 b := bufio.NewReader(i) 164 var count int64 165 166 for { 167 z, err := b.ReadByte() 168 if err != nil { 169 break 170 } 171 if z == o[0] { 172 count++ 173 } else { 174 return fmt.Errorf("Found non-matching byte: %v, at offset: %d", o[0], count) 175 } 176 } 177 178 if count == n { 179 return nil 180 } 181 return fmt.Errorf("Found %d count of %#v bytes, wanted to find %d count", count, o[0], n) 182 } 183 184 // BenchmarkDd benchmarks the dd command. Each "op" unit is a 1MiB block. 185 func BenchmarkDd(b *testing.B) { 186 tmpDir, execPath := testutil.CompileInTempDir(b) 187 defer os.RemoveAll(tmpDir) 188 189 const bytesPerOp = 1024 * 1024 190 b.SetBytes(bytesPerOp) 191 args := []string{ 192 "if=/dev/zero", 193 "of=/dev/null", 194 fmt.Sprintf("count=%d", b.N), 195 fmt.Sprintf("bs=%d", bytesPerOp), 196 } 197 b.ResetTimer() 198 if err := exec.Command(execPath, args...).Run(); err != nil { 199 b.Fatal(err) 200 } 201 }