fork of https://github.com/sourcegraph/zoekt
1package archive
2
3import (
4 "archive/tar"
5 "archive/zip"
6 "compress/gzip"
7 "context"
8 "errors"
9 "flag"
10 "fmt"
11 "io"
12 "log"
13 "os"
14 "path/filepath"
15 "strings"
16 "testing"
17 "time"
18
19 "github.com/stretchr/testify/require"
20
21 "github.com/sourcegraph/zoekt"
22 "github.com/sourcegraph/zoekt/index"
23 "github.com/sourcegraph/zoekt/query"
24 "github.com/sourcegraph/zoekt/search"
25)
26
27func TestMain(m *testing.M) {
28 flag.Parse()
29 if !testing.Verbose() {
30 log.SetOutput(io.Discard)
31 }
32 os.Exit(m.Run())
33}
34
35var modTime = time.Date(2024, 9, 26, 0, 0, 0, 0, time.UTC)
36
37func writeArchive(w io.Writer, format string, files map[string]string) (err error) {
38 if format == "zip" {
39 zw := zip.NewWriter(w)
40 for name, body := range files {
41 header := &zip.FileHeader{
42 Name: name,
43 Method: zip.Deflate,
44 Modified: modTime,
45 }
46 f, err := zw.CreateHeader(header)
47 if err != nil {
48 return err
49 }
50 if _, err := f.Write([]byte(body)); err != nil {
51 return err
52 }
53 }
54 return zw.Close()
55 }
56
57 if format == "tgz" {
58 gw := gzip.NewWriter(w)
59 defer func() {
60 err2 := gw.Close()
61 if err == nil {
62 err = err2
63 }
64 }()
65 w = gw
66 format = "tar"
67 }
68
69 if format != "tar" {
70 return errors.New("expected tar")
71 }
72
73 tw := tar.NewWriter(w)
74
75 for name, body := range files {
76 hdr := &tar.Header{
77 Name: name,
78 Mode: 0o600,
79 Size: int64(len(body)),
80 ModTime: modTime,
81 }
82 if err := tw.WriteHeader(hdr); err != nil {
83 return err
84 }
85 if _, err := tw.Write([]byte(body)); err != nil {
86 return err
87 }
88 }
89 if err := tw.Close(); err != nil {
90 return err
91 }
92
93 return nil
94}
95
96// TestIndexArg tests zoekt-archive-index by creating an archive and then
97// indexing and executing searches and checking we get expected results.
98// Additionally, we test that the index is properly updated with the
99// -incremental=true option changing the options between indexes and ensuring
100// the results change as expected.
101func TestIndexIncrementally(t *testing.T) {
102 for _, format := range []string{"tar", "tgz", "zip"} {
103 t.Run(format, func(t *testing.T) {
104 testIndexIncrementally(t, format)
105 })
106 }
107}
108
109func testIndexIncrementally(t *testing.T, format string) {
110 indexDir := t.TempDir()
111
112 archive, err := os.CreateTemp("", "TestIndexArg-archive")
113 if err != nil {
114 t.Fatalf("TempFile: %v", err)
115 }
116 defer os.Remove(archive.Name())
117
118 fileSize := 1000
119
120 files := map[string]string{}
121 for i := range 4 {
122 s := fmt.Sprintf("%d", i)
123 files["F"+s] = strings.Repeat("a", fileSize)
124 files["!F"+s] = strings.Repeat("a", fileSize)
125 }
126
127 err = writeArchive(archive, format, files)
128 if err != nil {
129 t.Fatalf("unable to create archive %v", err)
130 }
131 archive.Close()
132
133 // tests contain options used to build an index and the expected number of
134 // files in the result set based on the options.
135 tests := []struct {
136 largeFiles []string
137 wantNumFiles int
138 }{
139 {
140 largeFiles: []string{},
141 wantNumFiles: 0,
142 },
143 {
144 largeFiles: []string{"F0", "F2"},
145 wantNumFiles: 2,
146 },
147 {
148 largeFiles: []string{"F?", "!F2"},
149 wantNumFiles: 3,
150 },
151 {
152 largeFiles: []string{"F?", "!F2", "\\!F0"},
153 wantNumFiles: 4,
154 },
155 {
156 largeFiles: []string{"F?", "!F2", "\\!F0", "F2"},
157 wantNumFiles: 5,
158 },
159 }
160
161 for _, test := range tests {
162 largeFiles, wantNumFiles := test.largeFiles, test.wantNumFiles
163
164 bopts := index.Options{
165 SizeMax: fileSize - 1,
166 IndexDir: indexDir,
167 LargeFiles: largeFiles,
168 }
169 opts := Options{
170 Incremental: true,
171 Archive: archive.Name(),
172 Name: "repo",
173 Branch: "master",
174 Commit: "cccccccccccccccccccccccccccccccccccccccc",
175 Strip: 0,
176 }
177
178 if err := Index(opts, bopts); err != nil {
179 t.Fatalf("error creating index: %v", err)
180 }
181
182 ss, err := search.NewDirectorySearcher(indexDir)
183 if err != nil {
184 t.Fatalf("NewDirectorySearcher(%s): %v", indexDir, err)
185 }
186 defer ss.Close()
187
188 q, err := query.Parse("aaa")
189 if err != nil {
190 t.Fatalf("Parse(aaa): %v", err)
191 }
192
193 var sOpts zoekt.SearchOptions
194 result, err := ss.Search(context.Background(), q, &sOpts)
195 if err != nil {
196 t.Fatalf("Search(%v): %v", q, err)
197 }
198
199 if len(result.Files) != wantNumFiles {
200 t.Errorf("got %v, want %d files.", result.Files, wantNumFiles)
201 }
202 }
203}
204
205// TestLatestCommitDate tests that the latest commit date is set correctly if
206// the mod time of the files has been set during the archive creation.
207func TestLatestCommitDate(t *testing.T) {
208 for _, format := range []string{"tar", "tgz", "zip"} {
209 t.Run(format, func(t *testing.T) {
210 testLatestCommitDate(t, format)
211 })
212 }
213}
214
215func testLatestCommitDate(t *testing.T, format string) {
216 // Create an archive
217 archive, err := os.CreateTemp("", "TestLatestCommitDate")
218 require.NoError(t, err)
219 defer os.Remove(archive.Name())
220
221 fileSize := 10
222 files := map[string]string{}
223 for i := range 4 {
224 s := fmt.Sprintf("%d", i)
225 files["F"+s] = strings.Repeat("a", fileSize)
226 files["!F"+s] = strings.Repeat("a", fileSize)
227 }
228
229 err = writeArchive(archive, format, files)
230 if err != nil {
231 t.Fatalf("unable to create archive %v", err)
232 }
233 archive.Close()
234
235 // Index
236 indexDir := t.TempDir()
237 bopts := index.Options{
238 IndexDir: indexDir,
239 }
240 opts := Options{
241 Archive: archive.Name(),
242 Name: "repo",
243 Branch: "master",
244 Commit: "cccccccccccccccccccccccccccccccccccccccc",
245 }
246
247 err = Index(opts, bopts)
248 require.NoError(t, err)
249
250 // Read the metadata of the index we just created and check the latest commit date.
251 f, err := os.Open(indexDir)
252 require.NoError(t, err)
253
254 indexFiles, err := f.Readdirnames(1)
255 require.Len(t, indexFiles, 1)
256
257 repos, _, err := index.ReadMetadataPath(filepath.Join(indexDir, indexFiles[0]))
258 require.NoError(t, err)
259 require.Len(t, repos, 1)
260 require.True(t, repos[0].LatestCommitDate.Equal(modTime))
261}