diff options
Diffstat (limited to 'xdelta3/go')
-rw-r--r-- | xdelta3/go/src/regtest.go | 66 | ||||
-rw-r--r-- | xdelta3/go/src/xdelta/rstream.go | 23 | ||||
-rw-r--r-- | xdelta3/go/src/xdelta/run.go | 22 | ||||
-rw-r--r-- | xdelta3/go/src/xdelta/test.go | 132 | ||||
-rw-r--r-- | xdelta3/go/src/xdelta/tgroup.go | 93 |
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 ( | |||
11 | const ( | 11 | const ( |
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 | ||
18 | func smokeTest(r *xdelta.Runner, t *xdelta.TestGroup, p *xdelta.Program) { | 18 | func 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 | ||
58 | func offsetTest(r *xdelta.Runner, t *xdelta.TestGroup, p *xdelta.Program, bufsize, offset, length int64) { | 59 | func 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 | ||
93 | func main() { | 89 | func 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 | ||
13 | func WriteRstreams(t *TestGroup, seed, offset, len int64, | 13 | func 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 | ||
19 | func writeOne(t *TestGroup, seed, offset, len int64, stream io.WriteCloser) error { | 23 | func 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 | ||
35 | func writeRand(r *rand.Rand, len int64, s io.Writer) error { | 40 | func 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 @@ | |||
1 | package xdelta | ||
2 | |||
3 | import ( | ||
4 | "io/ioutil" | ||
5 | "os" | ||
6 | ) | ||
7 | |||
8 | type Runner struct { | ||
9 | Testdir string | ||
10 | } | ||
11 | |||
12 | func 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 | |||
20 | func (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 | ||
28 | type Runner struct { | ||
29 | Testdir string | ||
30 | } | ||
31 | |||
32 | type TestGroup struct { | ||
33 | sync.WaitGroup | ||
34 | sync.Mutex | ||
35 | errs []error | ||
36 | } | ||
37 | |||
38 | type Run struct { | 27 | type 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 | |||
48 | func (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 | |||
60 | func NewTestGroup() *TestGroup { | ||
61 | return &TestGroup{} | ||
62 | } | ||
63 | |||
64 | func (t *TestGroup) Drain(f io.ReadCloser, desc string) <-chan []byte { | 36 | func (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 | ||
80 | func (t *TestGroup) Empty(f io.ReadCloser, desc string) { | 50 | func (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 | ||
94 | func (t *TestGroup) Write(what string, f io.WriteCloser, b []byte) { | 66 | func 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 | ||
105 | func (t *TestGroup) CopyStreams(r io.ReadCloser, w io.WriteCloser) { | 78 | func (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 | ||
124 | func (t *TestGroup) CompareStreams(r1 io.ReadCloser, r2 io.ReadCloser, length int64) { | 99 | func (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 | ||
153 | func NewRunner() (*Runner, error) { | 129 | func (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 | |||
161 | func (r *Runner) Cleanup() { | ||
162 | os.RemoveAll(r.Testdir) | ||
163 | } | ||
164 | |||
165 | func (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 | ||
201 | func writeFifo(srcfile string, read io.Reader) error { | 174 | func 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 @@ | |||
1 | package xdelta | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "sync" | ||
6 | "time" | ||
7 | ) | ||
8 | |||
9 | type TestGroup struct { | ||
10 | *Runner | ||
11 | sync.Mutex | ||
12 | running []Goroutine | ||
13 | waitChan <-chan bool | ||
14 | } | ||
15 | |||
16 | type Goroutine struct { | ||
17 | name string | ||
18 | errChan chan error | ||
19 | } | ||
20 | |||
21 | func 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 | |||
29 | func (g *Goroutine) String() string { | ||
30 | return fmt.Sprint("[", g.name, "]") | ||
31 | } | ||
32 | |||
33 | func (g *Goroutine) OK() { | ||
34 | g.errChan <- nil | ||
35 | _ = <- g.errChan | ||
36 | } | ||
37 | |||
38 | func (g *Goroutine) Panic(err error) { | ||
39 | g.errChan <- err | ||
40 | _ = <- g.errChan | ||
41 | select {} | ||
42 | } | ||
43 | |||
44 | func (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 | |||
52 | func (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 | |||
61 | func 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 | } | ||