1package archive_test
2
3import (
4	"bytes"
5	"fmt"
6	"io"
7	"testing"
8
9	"github.com/cihub/seelog/archive"
10	"github.com/cihub/seelog/archive/gzip"
11	"github.com/cihub/seelog/archive/tar"
12	"github.com/cihub/seelog/archive/zip"
13	"github.com/cihub/seelog/io/iotest"
14)
15
16const (
17	gzipType = "gzip"
18	tarType  = "tar"
19	zipType  = "zip"
20)
21
22var types = []string{gzipType, tarType, zipType}
23
24type file struct {
25	name     string
26	contents []byte
27}
28
29var (
30	oneFile = []file{
31		{
32			name:     "file1",
33			contents: []byte("This is a single log."),
34		},
35	}
36	twoFiles = []file{
37		{
38			name:     "file1",
39			contents: []byte("This is a log."),
40		},
41		{
42			name:     "file2",
43			contents: []byte("This is another log."),
44		},
45	}
46)
47
48type testCase struct {
49	srcType, dstType string
50	in               []file
51}
52
53func copyTests() map[string]testCase {
54	// types X types X files
55	tests := make(map[string]testCase, len(types)*len(types)*2)
56	for _, srct := range types {
57		for _, dstt := range types {
58			tests[fmt.Sprintf("%s to %s: one file", srct, dstt)] = testCase{
59				srcType: srct,
60				dstType: dstt,
61				in:      oneFile,
62			}
63			// gzip does not handle more than one file
64			if srct != gzipType && dstt != gzipType {
65				tests[fmt.Sprintf("%s to %s: two files", srct, dstt)] = testCase{
66					srcType: srct,
67					dstType: dstt,
68					in:      twoFiles,
69				}
70			}
71		}
72	}
73	return tests
74}
75
76func TestCopy(t *testing.T) {
77	srcb, dstb := new(bytes.Buffer), new(bytes.Buffer)
78	for tname, tt := range copyTests() {
79		// Reset buffers between tests
80		srcb.Reset()
81		dstb.Reset()
82
83		// Last file name (needed for gzip.NewReader)
84		var fname string
85
86		// Seed the src
87		srcw := writer(t, tname, srcb, tt.srcType)
88		for _, f := range tt.in {
89			srcw.NextFile(f.name, iotest.FileInfo(t, f.contents))
90			mustCopy(t, tname, srcw, bytes.NewReader(f.contents))
91			fname = f.name
92		}
93		mustClose(t, tname, srcw)
94
95		// Perform the copy
96		srcr := reader(t, tname, srcb, tt.srcType, fname)
97		dstw := writer(t, tname, dstb, tt.dstType)
98		if err := archive.Copy(dstw, srcr); err != nil {
99			t.Fatalf("%s: %v", tname, err)
100		}
101		srcr.Close() // Read-only
102		mustClose(t, tname, dstw)
103
104		// Read back dst to confirm our expectations
105		dstr := reader(t, tname, dstb, tt.dstType, fname)
106		for _, want := range tt.in {
107			buf := new(bytes.Buffer)
108			name, err := dstr.NextFile()
109			if err != nil {
110				t.Fatalf("%s: %v", tname, err)
111			}
112			mustCopy(t, tname, buf, dstr)
113			got := file{
114				name:     name,
115				contents: buf.Bytes(),
116			}
117
118			switch {
119			case got.name != want.name:
120				t.Errorf("%s: got file %q but want file %q",
121					tname, got.name, want.name)
122
123			case !bytes.Equal(got.contents, want.contents):
124				t.Errorf("%s: mismatched contents in %q: got %q but want %q",
125					tname, got.name, got.contents, want.contents)
126			}
127		}
128		dstr.Close()
129	}
130}
131
132func writer(t *testing.T, tname string, w io.Writer, atype string) archive.WriteCloser {
133	switch atype {
134	case gzipType:
135		return gzip.NewWriter(w)
136	case tarType:
137		return tar.NewWriter(w)
138	case zipType:
139		return zip.NewWriter(w)
140	}
141	t.Fatalf("%s: unrecognized archive type: %s", tname, atype)
142	panic("execution continued after (*testing.T).Fatalf")
143}
144
145func reader(t *testing.T, tname string, buf *bytes.Buffer, atype string, fname string) archive.ReadCloser {
146	switch atype {
147	case gzipType:
148		gr, err := gzip.NewReader(buf, fname)
149		if err != nil {
150			t.Fatalf("%s: %v", tname, err)
151		}
152		return gr
153	case tarType:
154		return archive.NopCloser(tar.NewReader(buf))
155	case zipType:
156		zr, err := zip.NewReader(
157			bytes.NewReader(buf.Bytes()),
158			int64(buf.Len()))
159		if err != nil {
160			t.Fatalf("%s: new zip reader: %v", tname, err)
161		}
162		return archive.NopCloser(zr)
163	}
164	t.Fatalf("%s: unrecognized archive type: %s", tname, atype)
165	panic("execution continued after (*testing.T).Fatalf")
166}
167
168func mustCopy(t *testing.T, tname string, dst io.Writer, src io.Reader) {
169	if _, err := io.Copy(dst, src); err != nil {
170		t.Fatalf("%s: copy: %v", tname, err)
171	}
172}
173
174func mustClose(t *testing.T, tname string, c io.Closer) {
175	if err := c.Close(); err != nil {
176		t.Fatalf("%s: close: %v", tname, err)
177	}
178}
179