summaryrefslogtreecommitdiff
path: root/xdelta3/go/src/xdelta
diff options
context:
space:
mode:
Diffstat (limited to 'xdelta3/go/src/xdelta')
-rw-r--r--xdelta3/go/src/xdelta/rstream.go71
-rw-r--r--xdelta3/go/src/xdelta/run.go71
-rw-r--r--xdelta3/go/src/xdelta/test.go164
-rw-r--r--xdelta3/go/src/xdelta/tgroup.go97
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 @@
1package xdelta
2
3
4import (
5 "io"
6 "math/rand"
7)
8
9const (
10 blocksize = 1<<17
11)
12
13func (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
23func 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
46func 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
62func 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 @@
1package xdelta
2
3import (
4 "fmt"
5 "io"
6 "io/ioutil"
7 "os"
8 "os/exec"
9)
10
11type Program struct {
12 Path string
13}
14
15type 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
24type Runner struct {
25 Testdir string
26}
27
28func (r *Run) Wait() error {
29 return r.Cmd.Wait()
30}
31
32func 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
40func (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
49func (r *Runner) Cleanup() {
50 os.RemoveAll(r.Testdir)
51}
52
53func (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 @@
1package xdelta
2
3import (
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
17var (
18 tmpDir = "/tmp"
19 srcSeq int64
20)
21
22func (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
35func (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
50func (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
62func (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
81func (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
109func (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
144func (t *TestGroup) Fail(v ...interface{}) {
145 panic(fmt.Sprintln(v...))
146}
147
148func (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 @@
1package xdelta
2
3import (
4 "fmt"
5 "runtime"
6 "sync"
7)
8
9type 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
19type Goroutine struct {
20 *TestGroup
21 name string
22 done bool
23}
24
25func (g *Goroutine) String() string {
26 return fmt.Sprint("[", g.name, "]")
27}
28
29func (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
55func (g *Goroutine) OK() {
56 g.finish(nil)
57}
58
59func (g *Goroutine) Panic(err error) {
60 g.finish(err)
61 if g != g.TestGroup.main {
62 runtime.Goexit()
63 }
64}
65
66func (t *TestGroup) Main() *Goroutine { return t.main }
67
68func (t *TestGroup) Panic(err error) { t.Main().Panic(err) }
69
70func (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
80func (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}