diff options
Diffstat (limited to 'xdelta3/go/src/xdelta')
-rw-r--r-- | xdelta3/go/src/xdelta/rstream.go | 71 | ||||
-rw-r--r-- | xdelta3/go/src/xdelta/run.go | 71 | ||||
-rw-r--r-- | xdelta3/go/src/xdelta/test.go | 164 | ||||
-rw-r--r-- | xdelta3/go/src/xdelta/tgroup.go | 97 |
4 files changed, 403 insertions, 0 deletions
diff --git a/xdelta3/go/src/xdelta/rstream.go b/xdelta3/go/src/xdelta/rstream.go new file mode 100644 index 0000000..99c3d17 --- /dev/null +++ b/xdelta3/go/src/xdelta/rstream.go | |||
@@ -0,0 +1,71 @@ | |||
1 | package xdelta | ||
2 | |||
3 | |||
4 | import ( | ||
5 | "io" | ||
6 | "math/rand" | ||
7 | ) | ||
8 | |||
9 | const ( | ||
10 | blocksize = 1<<17 | ||
11 | ) | ||
12 | |||
13 | func (t *TestGroup) WriteRstreams(desc string, seed, offset, len int64, | ||
14 | src, tgt io.WriteCloser) { | ||
15 | t.Go("src-write:"+desc, func (g *Goroutine) { | ||
16 | writeOne(g, seed, 0, len, tgt, false) | ||
17 | }) | ||
18 | t.Go("tgt-write:"+desc, func (g *Goroutine) { | ||
19 | writeOne(g, seed, offset, len, src, true) | ||
20 | }) | ||
21 | } | ||
22 | |||
23 | func writeOne(g *Goroutine, seed, offset, len int64, stream io.WriteCloser, readall bool) { | ||
24 | if !readall { | ||
25 | // Allow the source-read to fail or block until the process terminates. | ||
26 | // This behavior is reserved for the decoder, which is not required to | ||
27 | // read the entire source. | ||
28 | g.OK() | ||
29 | } | ||
30 | if offset != 0 { | ||
31 | // Fill with other random data until the offset | ||
32 | if err := writeRand(g, rand.New(rand.NewSource(^seed)), offset, stream); err != nil { | ||
33 | g.Panic(err) | ||
34 | } | ||
35 | } | ||
36 | if err := writeRand(g, rand.New(rand.NewSource(seed)), | ||
37 | len - offset, stream); err != nil { | ||
38 | g.Panic(err) | ||
39 | } | ||
40 | if err := stream.Close(); err != nil { | ||
41 | g.Panic(err) | ||
42 | } | ||
43 | g.OK() | ||
44 | } | ||
45 | |||
46 | func writeRand(g *Goroutine, r *rand.Rand, len int64, s io.Writer) error { | ||
47 | blk := make([]byte, blocksize) | ||
48 | for len > 0 { | ||
49 | fillRand(r, blk) | ||
50 | c := blocksize | ||
51 | if len < blocksize { | ||
52 | c = int(len) | ||
53 | } | ||
54 | if _, err := s.Write(blk[0:c]); err != nil { | ||
55 | return err | ||
56 | } | ||
57 | len -= int64(c) | ||
58 | } | ||
59 | return nil | ||
60 | } | ||
61 | |||
62 | func fillRand(r *rand.Rand, blk []byte) { | ||
63 | for p := 0; p < len(blk); { | ||
64 | v := r.Int63() | ||
65 | for i := 7; i != 0 && p < len(blk); i-- { | ||
66 | blk[p] = byte(v) | ||
67 | p++ | ||
68 | v >>= 8 | ||
69 | } | ||
70 | } | ||
71 | } | ||
diff --git a/xdelta3/go/src/xdelta/run.go b/xdelta3/go/src/xdelta/run.go new file mode 100644 index 0000000..448fabe --- /dev/null +++ b/xdelta3/go/src/xdelta/run.go | |||
@@ -0,0 +1,71 @@ | |||
1 | package xdelta | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "io" | ||
6 | "io/ioutil" | ||
7 | "os" | ||
8 | "os/exec" | ||
9 | ) | ||
10 | |||
11 | type Program struct { | ||
12 | Path string | ||
13 | } | ||
14 | |||
15 | type Run struct { | ||
16 | Cmd exec.Cmd | ||
17 | Srcfile string | ||
18 | Stdin io.WriteCloser | ||
19 | Srcin io.WriteCloser | ||
20 | Stdout io.ReadCloser | ||
21 | Stderr io.ReadCloser | ||
22 | } | ||
23 | |||
24 | type Runner struct { | ||
25 | Testdir string | ||
26 | } | ||
27 | |||
28 | func (r *Run) Wait() error { | ||
29 | return r.Cmd.Wait() | ||
30 | } | ||
31 | |||
32 | func NewRunner() (*Runner, error) { | ||
33 | if dir, err := ioutil.TempDir(tmpDir, "xrt"); err != nil { | ||
34 | return nil, err | ||
35 | } else { | ||
36 | return &Runner{dir}, nil | ||
37 | } | ||
38 | } | ||
39 | |||
40 | func (r *Runner) newTestGroup(name string) (*TestGroup) { | ||
41 | tg := &TestGroup{Runner: r} | ||
42 | tg.WaitGroup.Add(1) | ||
43 | g0 := &Goroutine{tg, name, false} | ||
44 | tg.running = append(tg.running, g0) | ||
45 | tg.main = g0 | ||
46 | return tg | ||
47 | } | ||
48 | |||
49 | func (r *Runner) Cleanup() { | ||
50 | os.RemoveAll(r.Testdir) | ||
51 | } | ||
52 | |||
53 | func (r *Runner) RunTest(name string, f func (t *TestGroup)) { | ||
54 | t := r.newTestGroup(name) | ||
55 | c := make(chan interface{}) | ||
56 | go func() { | ||
57 | defer func() { | ||
58 | rec := recover() | ||
59 | c <- rec | ||
60 | }() | ||
61 | fmt.Println("Testing", name, "...") | ||
62 | f(t) | ||
63 | c <- nil | ||
64 | }() | ||
65 | rec := <- c | ||
66 | if t.errors == nil && rec == nil { | ||
67 | fmt.Println("Success:", name) | ||
68 | } else { | ||
69 | fmt.Println("FAILED:", name, t.errors, rec) | ||
70 | } | ||
71 | } | ||
diff --git a/xdelta3/go/src/xdelta/test.go b/xdelta3/go/src/xdelta/test.go new file mode 100644 index 0000000..7210698 --- /dev/null +++ b/xdelta3/go/src/xdelta/test.go | |||
@@ -0,0 +1,164 @@ | |||
1 | package xdelta | ||
2 | |||
3 | import ( | ||
4 | "bufio" | ||
5 | "bytes" | ||
6 | "errors" | ||
7 | "fmt" | ||
8 | "io" | ||
9 | "io/ioutil" | ||
10 | "os" | ||
11 | "path" | ||
12 | "sync/atomic" | ||
13 | |||
14 | "golang.org/x/sys/unix" | ||
15 | ) | ||
16 | |||
17 | var ( | ||
18 | tmpDir = "/tmp" | ||
19 | srcSeq int64 | ||
20 | ) | ||
21 | |||
22 | func (t *TestGroup) Drain(f io.ReadCloser, desc string) <-chan []byte { | ||
23 | c := make(chan []byte) | ||
24 | t.Go(desc, func(g *Goroutine) { | ||
25 | if b, err := ioutil.ReadAll(f); err != nil { | ||
26 | g.Panic(err) | ||
27 | } else { | ||
28 | c <- b | ||
29 | } | ||
30 | g.OK() | ||
31 | }) | ||
32 | return c | ||
33 | } | ||
34 | |||
35 | func (t *TestGroup) Empty(f io.ReadCloser, desc string) *Goroutine { | ||
36 | return t.Go("empty:"+desc, func (g *Goroutine) { | ||
37 | s := bufio.NewScanner(f) | ||
38 | for s.Scan() { | ||
39 | os.Stderr.Write([]byte(fmt.Sprint(desc, ": ", s.Text(), "\n"))) | ||
40 | } | ||
41 | err := s.Err() | ||
42 | f.Close() | ||
43 | if err != nil { | ||
44 | g.Panic(err) | ||
45 | } | ||
46 | g.OK() | ||
47 | }) | ||
48 | } | ||
49 | |||
50 | func (t *TestGroup) TestWrite(what string, f io.WriteCloser, b []byte) *Goroutine { | ||
51 | return t.Go("write", func(g *Goroutine) { | ||
52 | if _, err := f.Write(b); err != nil { | ||
53 | g.Panic(err) | ||
54 | } | ||
55 | if err := f.Close(); err != nil { | ||
56 | g.Panic(err) | ||
57 | } | ||
58 | g.OK() | ||
59 | }) | ||
60 | } | ||
61 | |||
62 | func (t *TestGroup) CopyStreams(r io.ReadCloser, w io.WriteCloser, written *int64) *Goroutine { | ||
63 | return t.Go("copy", func(g *Goroutine) { | ||
64 | nwrite, err := io.Copy(w, r) | ||
65 | if err != nil { | ||
66 | g.Panic(err) | ||
67 | } | ||
68 | err = r.Close() | ||
69 | if err != nil { | ||
70 | g.Panic(err) | ||
71 | } | ||
72 | err = w.Close() | ||
73 | if err != nil { | ||
74 | g.Panic(err) | ||
75 | } | ||
76 | g.OK() | ||
77 | *written = nwrite | ||
78 | }) | ||
79 | } | ||
80 | |||
81 | func (t *TestGroup) CompareStreams(r1 io.ReadCloser, r2 io.ReadCloser, length int64) *Goroutine { | ||
82 | return t.Go("compare", func(g *Goroutine) { | ||
83 | b1 := make([]byte, blocksize) | ||
84 | b2 := make([]byte, blocksize) | ||
85 | var idx int64 | ||
86 | for length > 0 { | ||
87 | c := blocksize | ||
88 | if length < blocksize { | ||
89 | c = int(length) | ||
90 | } | ||
91 | if _, err := io.ReadFull(r1, b1[0:c]); err != nil { | ||
92 | g.Panic(err) | ||
93 | } | ||
94 | if _, err := io.ReadFull(r2, b2[0:c]); err != nil { | ||
95 | g.Panic(err) | ||
96 | } | ||
97 | if bytes.Compare(b1[0:c], b2[0:c]) != 0 { | ||
98 | fmt.Println("B1 is", string(b1[0:c])) | ||
99 | fmt.Println("B2 is", string(b2[0:c])) | ||
100 | g.Panic(errors.New(fmt.Sprint("Bytes do not compare at ", idx))) | ||
101 | } | ||
102 | length -= int64(c) | ||
103 | idx += int64(c) | ||
104 | } | ||
105 | g.OK() | ||
106 | }) | ||
107 | } | ||
108 | |||
109 | func (t *TestGroup) Exec(desc string, p Program, srcfifo bool, flags []string) (*Run, error) { | ||
110 | var err error | ||
111 | run := &Run{} | ||
112 | args := []string{p.Path} | ||
113 | if srcfifo { | ||
114 | num := atomic.AddInt64(&srcSeq, 1) | ||
115 | run.Srcfile = path.Join(t.Runner.Testdir, fmt.Sprint("source", num)) | ||
116 | if err = unix.Mkfifo(run.Srcfile, 0600); err != nil { | ||
117 | return nil, err | ||
118 | } | ||
119 | read, write := io.Pipe() | ||
120 | t.writeFifo(run.Srcfile, read) | ||
121 | run.Srcin = write | ||
122 | args = append(args, "-s") | ||
123 | args = append(args, run.Srcfile) | ||
124 | } | ||
125 | if run.Stdin, err = run.Cmd.StdinPipe(); err != nil { | ||
126 | return nil, err | ||
127 | } | ||
128 | if run.Stdout, err = run.Cmd.StdoutPipe(); err != nil { | ||
129 | return nil, err | ||
130 | } | ||
131 | if run.Stderr, err = run.Cmd.StderrPipe(); err != nil { | ||
132 | return nil, err | ||
133 | } | ||
134 | |||
135 | run.Cmd.Path = p.Path | ||
136 | run.Cmd.Args = append(args, flags...) | ||
137 | run.Cmd.Dir = t.Runner.Testdir | ||
138 | if serr := run.Cmd.Start(); serr != nil { | ||
139 | return nil, serr | ||
140 | } | ||
141 | return run, nil | ||
142 | } | ||
143 | |||
144 | func (t *TestGroup) Fail(v ...interface{}) { | ||
145 | panic(fmt.Sprintln(v...)) | ||
146 | } | ||
147 | |||
148 | func (t *TestGroup) writeFifo(srcfile string, read io.Reader) *Goroutine { | ||
149 | return t.Go("compare", func(g *Goroutine) { | ||
150 | fifo, err := os.OpenFile(srcfile, os.O_WRONLY, 0600) | ||
151 | if err != nil { | ||
152 | fifo.Close() | ||
153 | g.Panic(err) | ||
154 | } | ||
155 | if _, err := io.Copy(fifo, read); err != nil { | ||
156 | fifo.Close() | ||
157 | g.Panic(err) | ||
158 | } | ||
159 | if err := fifo.Close(); err != nil { | ||
160 | g.Panic(err) | ||
161 | } | ||
162 | g.OK() | ||
163 | }) | ||
164 | } | ||
diff --git a/xdelta3/go/src/xdelta/tgroup.go b/xdelta3/go/src/xdelta/tgroup.go new file mode 100644 index 0000000..602b1e1 --- /dev/null +++ b/xdelta3/go/src/xdelta/tgroup.go | |||
@@ -0,0 +1,97 @@ | |||
1 | package xdelta | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "runtime" | ||
6 | "sync" | ||
7 | ) | ||
8 | |||
9 | type TestGroup struct { | ||
10 | *Runner | ||
11 | main *Goroutine | ||
12 | sync.Mutex | ||
13 | sync.WaitGroup | ||
14 | running []*Goroutine | ||
15 | errors []error | ||
16 | nonerrors []error // For tolerated / expected conditions | ||
17 | } | ||
18 | |||
19 | type Goroutine struct { | ||
20 | *TestGroup | ||
21 | name string | ||
22 | done bool | ||
23 | } | ||
24 | |||
25 | func (g *Goroutine) String() string { | ||
26 | return fmt.Sprint("[", g.name, "]") | ||
27 | } | ||
28 | |||
29 | func (g *Goroutine) finish(err error) { | ||
30 | wait := false | ||
31 | tg := g.TestGroup | ||
32 | sbuf := make([]byte, 4096) | ||
33 | sbuf = sbuf[0:runtime.Stack(sbuf, false)] | ||
34 | if err != nil { | ||
35 | err = fmt.Errorf("%v:%v:%v", g.name, err, string(sbuf)) | ||
36 | } | ||
37 | tg.Lock() | ||
38 | if g.done { | ||
39 | if err != nil { | ||
40 | tg.nonerrors = append(tg.nonerrors, err) | ||
41 | } | ||
42 | } else { | ||
43 | wait = true | ||
44 | g.done = true | ||
45 | if err != nil { | ||
46 | tg.errors = append(tg.errors, err) | ||
47 | } | ||
48 | } | ||
49 | tg.Unlock() | ||
50 | if wait { | ||
51 | tg.WaitGroup.Done() | ||
52 | } | ||
53 | } | ||
54 | |||
55 | func (g *Goroutine) OK() { | ||
56 | g.finish(nil) | ||
57 | } | ||
58 | |||
59 | func (g *Goroutine) Panic(err error) { | ||
60 | g.finish(err) | ||
61 | if g != g.TestGroup.main { | ||
62 | runtime.Goexit() | ||
63 | } | ||
64 | } | ||
65 | |||
66 | func (t *TestGroup) Main() *Goroutine { return t.main } | ||
67 | |||
68 | func (t *TestGroup) Panic(err error) { t.Main().Panic(err) } | ||
69 | |||
70 | func (t *TestGroup) Go(name string, f func(*Goroutine)) *Goroutine { | ||
71 | g := &Goroutine{t, name, false} | ||
72 | t.Lock() | ||
73 | t.WaitGroup.Add(1) | ||
74 | t.running = append(t.running, g) | ||
75 | t.Unlock() | ||
76 | go f(g) | ||
77 | return g | ||
78 | } | ||
79 | |||
80 | func (t *TestGroup) Wait(procs... *Run) { | ||
81 | t.Main().OK() | ||
82 | t.WaitGroup.Wait() | ||
83 | for _, p := range procs { | ||
84 | if err := p.Wait(); err != nil { | ||
85 | t.errors = append(t.errors, err) | ||
86 | } | ||
87 | } | ||
88 | for _, err := range t.errors { | ||
89 | fmt.Println(":ERROR:", err) | ||
90 | } | ||
91 | for _, err := range t.nonerrors { | ||
92 | fmt.Println("(ERROR)", err) | ||
93 | } | ||
94 | if len(t.errors) != 0 { | ||
95 | t.Fail("Test failed with", len(t.errors), "errors") | ||
96 | } | ||
97 | } | ||