summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoshua MacDonald <josh.macdonald@gmail.com>2015-11-21 23:36:51 -0800
committerJoshua MacDonald <josh.macdonald@gmail.com>2015-11-21 23:36:51 -0800
commitede482d8f3398262adcfb23095b6caad5872d8ba (patch)
tree89ea4cf3d61efbc82a9b342f3a9b6edc6edd8f69
parent6116f8879faff1a8145f55d9f537c33f39931678 (diff)
New TestGroup.Go method, improved error handling
-rw-r--r--xdelta3/go/src/regtest.go66
-rw-r--r--xdelta3/go/src/xdelta/rstream.go23
-rw-r--r--xdelta3/go/src/xdelta/run.go22
-rw-r--r--xdelta3/go/src/xdelta/test.go132
-rw-r--r--xdelta3/go/src/xdelta/tgroup.go93
5 files changed, 212 insertions, 124 deletions
diff --git a/xdelta3/go/src/regtest.go b/xdelta3/go/src/regtest.go
index b028ed5..793c9fc 100644
--- a/xdelta3/go/src/regtest.go
+++ b/xdelta3/go/src/regtest.go
@@ -11,61 +11,63 @@ import (
11const ( 11const (
12 blocksize = 1<<16 12 blocksize = 1<<16
13 winsize = 1<<22 13 winsize = 1<<22
14 xdelta3 = "/volume/home/jmacd/src/xdelta-devel/xdelta3/xdelta3" 14 xdelta3 = "/Users/jmacd/src/xdelta-devel/xdelta3/xdelta3"
15 seed = 1422253499919909358 15 seed = 1422253499919909358
16) 16)
17 17
18func smokeTest(r *xdelta.Runner, t *xdelta.TestGroup, p *xdelta.Program) { 18func smokeTest(r *xdelta.Runner, p *xdelta.Program) {
19 t.Add(1) 19 t, g := xdelta.NewTestGroup(r)
20 target := "Hello world!" 20 target := "Hello world!"
21 source := "Hello world, nice to meet you!" 21 source := "Hello world, nice to meet you!"
22 22
23 enc, err := r.Exec(p, true, []string{"-evv"}) 23 enc, err := t.Exec(p, true, []string{"-evv"})
24 if err != nil { 24 if err != nil {
25 t.Panic(err) 25 g.Panic(err)
26 } 26 }
27 encodeout := t.Drain(enc.Stdout, "encode.stdout") 27 encodeout := t.Drain(enc.Stdout, "encode.stdout")
28 t.Empty(enc.Stderr, "encode") 28 t.Empty(enc.Stderr, "encode")
29 29
30 t.Write("encode.stdin", enc.Stdin, []byte(target)) 30 if err := xdelta.TestWrite("encode.stdin", enc.Stdin, []byte(target)); err != nil {
31 t.Write("encode.srcin", enc.Srcin, []byte(source)) 31 g.Panic(err)
32 32 }
33 if err := enc.Cmd.Wait(); err != nil { 33 if err := xdelta.TestWrite("encode.srcin", enc.Srcin, []byte(source)); err != nil {
34 t.Panic(err) 34 g.Panic(err)
35 } 35 }
36 36
37 dec, err := r.Exec(p, true, []string{"-dvv"}) 37 dec, err := t.Exec(p, true, []string{"-dvv"})
38 if err != nil { 38 if err != nil {
39 t.Panic(err) 39 g.Panic(err)
40 } 40 }
41 41
42 decodeout := t.Drain(dec.Stdout, "decode.stdout") 42 decodeout := t.Drain(dec.Stdout, "decode.stdout")
43 t.Empty(dec.Stderr, "decode") 43 t.Empty(dec.Stderr, "decode")
44 44
45 t.Write("decode.stdin", dec.Stdin, <-encodeout) 45 if err := xdelta.TestWrite("decode.stdin", dec.Stdin, <-encodeout); err != nil {
46 t.Write("decode.srcin", dec.Srcin, []byte(source)) 46 g.Panic(err)
47 decoded := string(<-decodeout)
48 if err := dec.Cmd.Wait(); err != nil {
49 t.Panic(err)
50 } 47 }
48 if err := xdelta.TestWrite("decode.srcin", dec.Srcin, []byte(source)); err != nil {
49 g.Panic(err)
50 }
51 decoded := string(<-decodeout)
51 if decoded != target { 52 if decoded != target {
52 t.Panic(errors.New("It's not working!!!")) 53 g.Panic(errors.New("It's not working!!!"))
53 } 54 }
54 t.Done() 55 t.Wait(g)
55 fmt.Println("Smoketest pass") 56 fmt.Println("Smoketest pass")
56} 57}
57 58
58func offsetTest(r *xdelta.Runner, t *xdelta.TestGroup, p *xdelta.Program, bufsize, offset, length int64) { 59func offsetTest(r *xdelta.Runner, p *xdelta.Program, bufsize, offset, length int64) {
59 t.Add(1) 60 t, g := xdelta.NewTestGroup(r)
60 eargs := []string{"-e", "-0", fmt.Sprint("-B", bufsize), "-vv", fmt.Sprint("-W", winsize)} 61 eargs := []string{"-e", "-0", fmt.Sprint("-B", bufsize), "-vv", fmt.Sprint("-W", winsize)}
61 enc, err := r.Exec(p, true, eargs) 62 enc, err := t.Exec(p, true, eargs)
62 if err != nil { 63 if err != nil {
63 t.Panic(err) 64 g.Panic(err)
64 } 65 }
66
65 dargs := []string{"-d", fmt.Sprint("-B", bufsize), "-vv", fmt.Sprint("-W", winsize)} 67 dargs := []string{"-d", fmt.Sprint("-B", bufsize), "-vv", fmt.Sprint("-W", winsize)}
66 dec, err := r.Exec(p, true, dargs) 68 dec, err := t.Exec(p, true, dargs)
67 if err != nil { 69 if err != nil {
68 t.Panic(err) 70 g.Panic(err)
69 } 71 }
70 72
71 read, write := io.Pipe() 73 read, write := io.Pipe()
@@ -81,13 +83,7 @@ func offsetTest(r *xdelta.Runner, t *xdelta.TestGroup, p *xdelta.Program, bufsiz
81 xdelta.WriteRstreams(t, seed, offset, length, enc.Srcin, enc.Stdin) 83 xdelta.WriteRstreams(t, seed, offset, length, enc.Srcin, enc.Stdin)
82 xdelta.WriteRstreams(t, seed, offset, length, dec.Srcin, write) 84 xdelta.WriteRstreams(t, seed, offset, length, dec.Srcin, write)
83 85
84 if err := enc.Cmd.Wait(); err != nil { 86 t.Wait(g)
85 t.Panic(err)
86 }
87 if err := dec.Cmd.Wait(); err != nil {
88 t.Panic(err)
89 }
90 t.Done()
91} 87}
92 88
93func main() { 89func main() {
@@ -99,8 +95,8 @@ func main() {
99 95
100 prog := &xdelta.Program{xdelta3} 96 prog := &xdelta.Program{xdelta3}
101 97
102 smokeTest(r, xdelta.NewTestGroup(), prog) 98 smokeTest(r, prog)
103 offsetTest(r, xdelta.NewTestGroup(), prog, 4 << 20, 3 << 20, 5 << 20) 99 offsetTest(r, prog, 4 << 20, 3 << 20, 5 << 20)
104 100
105 //offsetTest(r, xdelta.NewTestGroup(), prog, 1 << 31, 1 << 32, 1 << 33) 101 //offsetTest(r, xdelta.NewTestGroup(), prog, 1 << 31, 1 << 32, 1 << 33)
106} 102}
diff --git a/xdelta3/go/src/xdelta/rstream.go b/xdelta3/go/src/xdelta/rstream.go
index 3e7265f..3f520d7 100644
--- a/xdelta3/go/src/xdelta/rstream.go
+++ b/xdelta3/go/src/xdelta/rstream.go
@@ -11,25 +11,30 @@ const (
11) 11)
12 12
13func WriteRstreams(t *TestGroup, seed, offset, len int64, 13func WriteRstreams(t *TestGroup, seed, offset, len int64,
14 first, second io.WriteCloser) { 14 src, tgt io.WriteCloser) {
15 go writeOne(t, seed, 0, len, first) 15 t.Go("src", func (g Goroutine) {
16 go writeOne(t, seed, offset, len, second) 16 go writeOne(g, seed, 0, len, src)
17 })
18 t.Go("tgt", func (g Goroutine) {
19 go writeOne(g, seed, offset, len, tgt)
20 })
17} 21}
18 22
19func writeOne(t *TestGroup, seed, offset, len int64, stream io.WriteCloser) error { 23func writeOne(g Goroutine, seed, offset, len int64, stream io.WriteCloser) {
20 t.WaitGroup.Add(1)
21 if offset != 0 { 24 if offset != 0 {
22 // Fill with other random data until the offset 25 // Fill with other random data until the offset
23 if err := writeRand(rand.New(rand.NewSource(^seed)), offset, stream); err != nil { 26 if err := writeRand(rand.New(rand.NewSource(^seed)), offset, stream); err != nil {
24 return err 27 g.Panic(err)
25 } 28 }
26 } 29 }
27 if err := writeRand(rand.New(rand.NewSource(seed)), 30 if err := writeRand(rand.New(rand.NewSource(seed)),
28 len - offset, stream); err != nil { 31 len - offset, stream); err != nil {
29 return err 32 g.Panic(err)
33 }
34 if err := stream.Close(); err != nil {
35 g.Panic(err)
30 } 36 }
31 t.WaitGroup.Done() 37 g.OK()
32 return stream.Close()
33} 38}
34 39
35func writeRand(r *rand.Rand, len int64, s io.Writer) error { 40func writeRand(r *rand.Rand, len int64, s io.Writer) error {
diff --git a/xdelta3/go/src/xdelta/run.go b/xdelta3/go/src/xdelta/run.go
new file mode 100644
index 0000000..f9b4185
--- /dev/null
+++ b/xdelta3/go/src/xdelta/run.go
@@ -0,0 +1,22 @@
1package xdelta
2
3import (
4 "io/ioutil"
5 "os"
6)
7
8type Runner struct {
9 Testdir string
10}
11
12func NewRunner() (*Runner, error) {
13 if dir, err := ioutil.TempDir(tmpDir, "xrt"); err != nil {
14 return nil, err
15 } else {
16 return &Runner{dir}, nil
17 }
18}
19
20func (r *Runner) Cleanup() {
21 os.RemoveAll(r.Testdir)
22}
diff --git a/xdelta3/go/src/xdelta/test.go b/xdelta3/go/src/xdelta/test.go
index e853554..d11ec29 100644
--- a/xdelta3/go/src/xdelta/test.go
+++ b/xdelta3/go/src/xdelta/test.go
@@ -11,7 +11,6 @@ import (
11 "os/exec" 11 "os/exec"
12 "path" 12 "path"
13 "sync/atomic" 13 "sync/atomic"
14 "sync"
15 14
16 "golang.org/x/sys/unix" 15 "golang.org/x/sys/unix"
17) 16)
@@ -25,16 +24,6 @@ type Program struct {
25 Path string 24 Path string
26} 25}
27 26
28type Runner struct {
29 Testdir string
30}
31
32type TestGroup struct {
33 sync.WaitGroup
34 sync.Mutex
35 errs []error
36}
37
38type Run struct { 27type Run struct {
39 Cmd exec.Cmd 28 Cmd exec.Cmd
40 Srcfile string 29 Srcfile string
@@ -44,86 +33,71 @@ type Run struct {
44 Stderr io.ReadCloser 33 Stderr io.ReadCloser
45} 34}
46 35
47
48func (t *TestGroup) Panic(err error) {
49 t.Lock()
50 t.errs = append(t.errs, err)
51 t.Unlock()
52 t.Done() // For the caller
53 t.Wait()
54 for _, e := range t.errs {
55 fmt.Println(e)
56 }
57 panic(fmt.Sprintf("%d errors", len(t.errs)))
58}
59
60func NewTestGroup() *TestGroup {
61 return &TestGroup{}
62}
63
64func (t *TestGroup) Drain(f io.ReadCloser, desc string) <-chan []byte { 36func (t *TestGroup) Drain(f io.ReadCloser, desc string) <-chan []byte {
65 c := make(chan []byte) 37 c := make(chan []byte)
66 go func() { 38 t.Go(desc, func(g Goroutine) {
67 t.Add(1)
68 //fmt.Println("Draining", desc)
69 if b, err := ioutil.ReadAll(f); err != nil { 39 if b, err := ioutil.ReadAll(f); err != nil {
70 t.Panic(err) 40 fmt.Println("Drain", err)
41 g.Panic(err)
71 } else { 42 } else {
72 //fmt.Println("Draining", desc, "--got it")
73 c <- b 43 c <- b
74 } 44 }
75 t.Done() 45 g.OK()
76 }() 46 })
77 return c 47 return c
78} 48}
79 49
80func (t *TestGroup) Empty(f io.ReadCloser, desc string) { 50func (t *TestGroup) Empty(f io.ReadCloser, desc string) {
81 go func() { 51 t.Go(desc, func (g Goroutine) {
82 t.Add(1)
83 s := bufio.NewScanner(f) 52 s := bufio.NewScanner(f)
84 for s.Scan() { 53 for s.Scan() {
85 os.Stderr.Write([]byte(fmt.Sprint(desc, ": ", s.Text(), "\n"))) 54 os.Stderr.Write([]byte(fmt.Sprint(desc, ": ", s.Text(), "\n")))
86 } 55 }
87 if err := s.Err(); err != nil { 56 err := s.Err()
88 t.Panic(err) 57 f.Close()
58 if err != nil {
59 fmt.Println("Empty", desc, err)
60 g.Panic(err)
89 } 61 }
90 t.Done() 62 g.OK()
91 }() 63 })
92} 64}
93 65
94func (t *TestGroup) Write(what string, f io.WriteCloser, b []byte) { 66func TestWrite(what string, f io.WriteCloser, b []byte) error {
95 //fmt.Println("Write (", what, ") ", len(b), "bytes")
96 if _, err := f.Write(b); err != nil { 67 if _, err := f.Write(b); err != nil {
97 t.Panic(errors.New(fmt.Sprint(what, ":", err))) 68 fmt.Println("Write", err)
69 return errors.New(fmt.Sprint(what, ":", err))
98 } 70 }
99 //fmt.Println("Write (", what, ") closing")
100 if err := f.Close(); err != nil { 71 if err := f.Close(); err != nil {
101 t.Panic(errors.New(fmt.Sprint(what, ":", err))) 72 fmt.Println("Close", err)
73 return errors.New(fmt.Sprint(what, ":", err))
102 } 74 }
75 return nil
103} 76}
104 77
105func (t *TestGroup) CopyStreams(r io.ReadCloser, w io.WriteCloser) { 78func (t *TestGroup) CopyStreams(r io.ReadCloser, w io.WriteCloser) {
106 go func() { 79 t.Go("copy", func(g Goroutine) {
107 t.Add(1)
108 _, err := io.Copy(w, r) 80 _, err := io.Copy(w, r)
109 if err != nil { 81 if err != nil {
110 t.Panic(err) 82 fmt.Println("CopyS", err)
83 g.Panic(err)
111 } 84 }
112 err = r.Close() 85 err = r.Close()
113 if err != nil { 86 if err != nil {
114 t.Panic(err) 87 fmt.Println("CloseS1", err)
88 g.Panic(err)
115 } 89 }
116 err = w.Close() 90 err = w.Close()
117 if err != nil { 91 if err != nil {
118 t.Panic(err) 92 fmt.Println("CloseS2", err)
93 g.Panic(err)
119 } 94 }
120 t.Done() 95 g.OK()
121 }() 96 })
122} 97}
123 98
124func (t *TestGroup) CompareStreams(r1 io.ReadCloser, r2 io.ReadCloser, length int64) { 99func (t *TestGroup) CompareStreams(r1 io.ReadCloser, r2 io.ReadCloser, length int64) {
125 go func() { 100 t.Go("compare", func(g Goroutine) {
126 t.Add(1)
127 b1 := make([]byte, blocksize) 101 b1 := make([]byte, blocksize)
128 b2 := make([]byte, blocksize) 102 b2 := make([]byte, blocksize)
129 var idx int64 103 var idx int64
@@ -133,42 +107,32 @@ func (t *TestGroup) CompareStreams(r1 io.ReadCloser, r2 io.ReadCloser, length in
133 c = int(length) 107 c = int(length)
134 } 108 }
135 if _, err := io.ReadFull(r1, b1[0:c]); err != nil { 109 if _, err := io.ReadFull(r1, b1[0:c]); err != nil {
136 t.Panic(err) 110 fmt.Println("ReadFull1", err)
111 g.Panic(err)
137 } 112 }
138 if _, err := io.ReadFull(r2, b2[0:c]); err != nil { 113 if _, err := io.ReadFull(r2, b2[0:c]); err != nil {
139 t.Panic(err) 114 fmt.Println("ReadFull2", err)
115 g.Panic(err)
140 } 116 }
141 if bytes.Compare(b1[0:c], b2[0:c]) != 0 { 117 if bytes.Compare(b1[0:c], b2[0:c]) != 0 {
142 fmt.Println("B1 is", string(b1[0:c])) 118 fmt.Println("B1 is", string(b1[0:c]))
143 fmt.Println("B2 is", string(b2[0:c])) 119 fmt.Println("B2 is", string(b2[0:c]))
144 t.Panic(errors.New(fmt.Sprint("Bytes do not compare at ", idx))) 120 g.Panic(errors.New(fmt.Sprint("Bytes do not compare at ", idx)))
145 } 121 }
146 length -= int64(c) 122 length -= int64(c)
147 idx += int64(c) 123 idx += int64(c)
148 } 124 }
149 t.Done() 125 g.OK()
150 }() 126 })
151} 127}
152 128
153func NewRunner() (*Runner, error) { 129func (t *TestGroup) Exec(p *Program, srcfifo bool, flags []string) (*Run, error) {
154 if dir, err := ioutil.TempDir(tmpDir, "xrt"); err != nil {
155 return nil, err
156 } else {
157 return &Runner{dir}, nil
158 }
159}
160
161func (r *Runner) Cleanup() {
162 os.RemoveAll(r.Testdir)
163}
164
165func (r *Runner) Exec(p *Program, srcfifo bool, flags []string) (*Run, error) {
166 var err error 130 var err error
167 run := &Run{} 131 run := &Run{}
168 args := []string{p.Path} 132 args := []string{p.Path}
169 if srcfifo { 133 if srcfifo {
170 num := atomic.AddInt64(&srcSeq, 1) 134 num := atomic.AddInt64(&srcSeq, 1)
171 run.Srcfile = path.Join(r.Testdir, fmt.Sprint("source", num)) 135 run.Srcfile = path.Join(t.Runner.Testdir, fmt.Sprint("source", num))
172 if err = unix.Mkfifo(run.Srcfile, 0600); err != nil { 136 if err = unix.Mkfifo(run.Srcfile, 0600); err != nil {
173 return nil, err 137 return nil, err
174 } 138 }
@@ -193,21 +157,29 @@ func (r *Runner) Exec(p *Program, srcfifo bool, flags []string) (*Run, error) {
193 157
194 run.Cmd.Path = p.Path 158 run.Cmd.Path = p.Path
195 run.Cmd.Args = append(args, flags...) 159 run.Cmd.Args = append(args, flags...)
196 run.Cmd.Dir = r.Testdir 160 run.Cmd.Dir = t.Runner.Testdir
197 161
198 return run, run.Cmd.Start() 162 if serr := run.Cmd.Start(); serr != nil {
163 return nil, serr
164 }
165 t.Go("exec-wait", func (g Goroutine) {
166 if err := run.Cmd.Wait(); err != nil {
167 g.Panic(err)
168 }
169 g.OK()
170 })
171 return run, nil
199} 172}
200 173
201func writeFifo(srcfile string, read io.Reader) error { 174func writeFifo(srcfile string, read io.Reader) error {
202 fifo, err := os.OpenFile(srcfile, os.O_WRONLY, 0600) 175 fifo, err := os.OpenFile(srcfile, os.O_WRONLY, 0600)
203 if err != nil { 176 if err != nil {
177 fifo.Close()
204 return err 178 return err
205 } 179 }
206 if _, err := io.Copy(fifo, read); err != nil { 180 if _, err := io.Copy(fifo, read); err != nil {
181 fifo.Close()
207 return err 182 return err
208 } 183 }
209 if err := fifo.Close(); err != nil { 184 return fifo.Close()
210 return err
211 }
212 return nil
213} 185}
diff --git a/xdelta3/go/src/xdelta/tgroup.go b/xdelta3/go/src/xdelta/tgroup.go
new file mode 100644
index 0000000..bb34258
--- /dev/null
+++ b/xdelta3/go/src/xdelta/tgroup.go
@@ -0,0 +1,93 @@
1package xdelta
2
3import (
4 "fmt"
5 "sync"
6 "time"
7)
8
9type TestGroup struct {
10 *Runner
11 sync.Mutex
12 running []Goroutine
13 waitChan <-chan bool
14}
15
16type Goroutine struct {
17 name string
18 errChan chan error
19}
20
21func NewTestGroup(r *Runner) (*TestGroup, Goroutine) {
22 g := Goroutine{"main", make(chan error)}
23 wc := make(chan bool)
24 tg := &TestGroup{Runner: r, running: []Goroutine{g}, waitChan: wc}
25 go waitAll(tg, wc)
26 return tg, g
27}
28
29func (g *Goroutine) String() string {
30 return fmt.Sprint("[", g.name, "]")
31}
32
33func (g *Goroutine) OK() {
34 g.errChan <- nil
35 _ = <- g.errChan
36}
37
38func (g *Goroutine) Panic(err error) {
39 g.errChan <- err
40 _ = <- g.errChan
41 select {}
42}
43
44func (t *TestGroup) Go(name string, f func(Goroutine)) {
45 g := Goroutine{name, make(chan error)}
46 t.Lock()
47 t.running = append(t.running, g)
48 t.Unlock()
49 go f(g)
50}
51
52func (t *TestGroup) Wait(g Goroutine) {
53 g.OK()
54 t.Lock()
55 t.waitChan = nil
56 wc := t.waitChan
57 t.Unlock()
58 _ = <- wc
59}
60
61func waitAll(t *TestGroup, wc chan bool) {
62 for {
63 t.Lock()
64 if len(t.running) == 0 {
65 t.Unlock()
66 break
67 }
68 runner := t.running[0]
69 t.running = t.running[1:]
70 t.Unlock()
71
72 timeout := make(chan bool, 1)
73 go func() {
74 time.Sleep(1 * time.Second)
75 timeout <- true
76 }()
77 fmt.Println("Waiting on", runner)
78 select {
79 case err := <- runner.errChan:
80 runner.errChan <- err
81 if err != nil {
82 fmt.Println("[G]", runner, err)
83 } else {
84 fmt.Println("[G]", runner, "OK")
85 }
86 case <- timeout:
87 t.Lock()
88 t.running = append(t.running, runner)
89 t.Unlock()
90 }
91 }
92 wc <- true
93}