1 package integration_tests_test
2
3 import (
4 "bytes"
5 "fmt"
6 "io"
7 "io/ioutil"
8 "os"
9 "os/exec"
10 "path/filepath"
11 "regexp"
12 "runtime"
13 "strings"
14 "testing"
15 )
16
17 const (
18 infoLog = "this is a info log line"
19 warningLog = "this is a warning log line"
20 errorLog = "this is a error log line"
21 fatalLog = "this is a fatal log line"
22 )
23
24
25 type res = []*regexp.Regexp
26
27 var (
28 infoLogRE = regexp.MustCompile(regexp.QuoteMeta(infoLog))
29 warningLogRE = regexp.MustCompile(regexp.QuoteMeta(warningLog))
30 errorLogRE = regexp.MustCompile(regexp.QuoteMeta(errorLog))
31 fatalLogRE = regexp.MustCompile(regexp.QuoteMeta(fatalLog))
32
33 stackTraceRE = regexp.MustCompile(`\ngoroutine \d+ \[[^]]+\]:\n`)
34
35 allLogREs = res{infoLogRE, warningLogRE, errorLogRE, fatalLogRE, stackTraceRE}
36
37 defaultExpectedInDirREs = map[int]res{
38 0: {stackTraceRE, fatalLogRE, errorLogRE, warningLogRE, infoLogRE},
39 1: {stackTraceRE, fatalLogRE, errorLogRE, warningLogRE},
40 2: {stackTraceRE, fatalLogRE, errorLogRE},
41 3: {stackTraceRE, fatalLogRE},
42 }
43 expectedOneOutputInDirREs = map[int]res{
44 0: {infoLogRE},
45 1: {warningLogRE},
46 2: {errorLogRE},
47 3: {fatalLogRE},
48 }
49
50 defaultNotExpectedInDirREs = map[int]res{
51 0: {},
52 1: {infoLogRE},
53 2: {infoLogRE, warningLogRE},
54 3: {infoLogRE, warningLogRE, errorLogRE},
55 }
56 )
57
58 func TestDestinationsWithDifferentFlags(t *testing.T) {
59 tests := map[string]struct {
60
61 logfile bool
62
63 logdir bool
64
65 flags []string
66
67
68
69
70 expectedLogFile bool
71
72
73
74
75 expectedLogDir bool
76
77
78 expectedOnStderr res
79
80 notExpectedOnStderr res
81
82 expectedInFile res
83
84 notExpectedInFile res
85
86
87
88 expectedInDir map[int]res
89
90
91 notExpectedInDir map[int]res
92 }{
93 "default flags": {
94
95
96 expectedOnStderr: res{infoLogRE, warningLogRE, errorLogRE, fatalLogRE},
97 notExpectedOnStderr: res{stackTraceRE},
98 },
99 "everything disabled": {
100
101
102 flags: []string{"-logtostderr=false", "-alsologtostderr=false", "-stderrthreshold=1000"},
103
104 notExpectedOnStderr: allLogREs,
105 },
106 "everything disabled but low stderrthreshold": {
107
108
109
110 flags: []string{"-logtostderr=false", "-alsologtostderr=false", "-stderrthreshold=1"},
111
112 expectedOnStderr: res{warningLogRE, errorLogRE, stackTraceRE},
113 notExpectedOnStderr: res{infoLogRE},
114 },
115 "with logtostderr only": {
116
117
118 flags: []string{"-logtostderr=true", "-alsologtostderr=false", "-stderrthreshold=1000"},
119
120 expectedOnStderr: res{infoLogRE, warningLogRE, errorLogRE, fatalLogRE},
121 notExpectedOnStderr: res{stackTraceRE},
122 },
123 "with log file only": {
124
125
126 logfile: true,
127 flags: []string{"-logtostderr=false", "-alsologtostderr=false", "-stderrthreshold=1000"},
128
129 expectedLogFile: true,
130
131 notExpectedOnStderr: allLogREs,
132 expectedInFile: allLogREs,
133 },
134 "with log dir only": {
135
136
137 logdir: true,
138 flags: []string{"-logtostderr=false", "-alsologtostderr=false", "-stderrthreshold=1000"},
139
140 expectedLogDir: true,
141
142 notExpectedOnStderr: allLogREs,
143 expectedInDir: defaultExpectedInDirREs,
144 notExpectedInDir: defaultNotExpectedInDirREs,
145 },
146 "with log dir only and one_output": {
147
148
149 logdir: true,
150 flags: []string{"-logtostderr=false", "-alsologtostderr=false", "-stderrthreshold=1000", "-one_output=true"},
151
152 expectedLogDir: true,
153
154 notExpectedOnStderr: allLogREs,
155 expectedInDir: expectedOneOutputInDirREs,
156 notExpectedInDir: defaultNotExpectedInDirREs,
157 },
158 "with log dir and logtostderr": {
159
160
161
162 logdir: true,
163 flags: []string{"-logtostderr=true", "-alsologtostderr=false", "-stderrthreshold=1000"},
164
165 expectedOnStderr: res{infoLogRE, warningLogRE, errorLogRE, fatalLogRE},
166 notExpectedOnStderr: res{stackTraceRE},
167 },
168 "with log file and log dir": {
169
170
171
172 logdir: true,
173 logfile: true,
174 flags: []string{"-logtostderr=false", "-alsologtostderr=false", "-stderrthreshold=1000"},
175
176 expectedLogFile: true,
177
178 notExpectedOnStderr: allLogREs,
179 expectedInFile: allLogREs,
180 },
181 "with log file and alsologtostderr": {
182
183
184
185 flags: []string{"-alsologtostderr=true", "-logtostderr=false", "-stderrthreshold=1000"},
186 logfile: true,
187
188 expectedLogFile: true,
189
190 expectedOnStderr: allLogREs,
191 expectedInFile: allLogREs,
192 },
193 "with log dir and alsologtostderr": {
194
195
196
197 logdir: true,
198 flags: []string{"-alsologtostderr=true", "-logtostderr=false", "-stderrthreshold=1000"},
199
200 expectedLogDir: true,
201
202 expectedOnStderr: allLogREs,
203 expectedInDir: defaultExpectedInDirREs,
204 notExpectedInDir: defaultNotExpectedInDirREs,
205 },
206 "with log dir, alsologtostderr and one_output": {
207
208
209
210 logdir: true,
211 flags: []string{"-alsologtostderr=true", "-logtostderr=false", "-stderrthreshold=1000", "-one_output=true"},
212
213 expectedLogDir: true,
214
215 expectedOnStderr: allLogREs,
216 expectedInDir: expectedOneOutputInDirREs,
217 notExpectedInDir: defaultNotExpectedInDirREs,
218 },
219 }
220
221 binaryFileExtention := ""
222 if runtime.GOOS == "windows" {
223 binaryFileExtention = ".exe"
224 }
225
226 for tcName, tc := range tests {
227 tc := tc
228 t.Run(tcName, func(t *testing.T) {
229 t.Parallel()
230 withTmpDir(t, func(logdir string) {
231
232 flags := tc.flags
233 stderr := &bytes.Buffer{}
234 logfile := filepath.Join(logdir, "the_single_log_file")
235
236 if tc.logfile {
237 flags = append(flags, "-log_file="+logfile)
238 }
239 if tc.logdir {
240 flags = append(flags, "-log_dir="+logdir)
241 }
242
243
244 klogRun(t, flags, stderr)
245
246
247
248 checkForLogs(t, tc.expectedOnStderr, tc.notExpectedOnStderr, stderr.String(), "stderr")
249
250
251 if tc.expectedLogFile {
252 content := getFileContent(t, logfile)
253 checkForLogs(t, tc.expectedInFile, tc.notExpectedInFile, content, "logfile")
254 } else {
255 assertFileIsAbsent(t, logfile)
256 }
257
258
259 for level, levelName := range logFileLevels {
260 binaryName := "main" + binaryFileExtention
261 logfile, err := getLogFilePath(logdir, binaryName, levelName)
262 if tc.expectedLogDir {
263 if err != nil {
264 t.Errorf("Unable to find log file: %v", err)
265 }
266 content := getFileContent(t, logfile)
267 checkForLogs(t, tc.expectedInDir[level], tc.notExpectedInDir[level], content, "logfile["+logfile+"]")
268 } else {
269 if err == nil {
270 t.Errorf("Unexpectedly found log file %s", logfile)
271 }
272 }
273 }
274 })
275 })
276 }
277 }
278
279 const klogExampleGoFile = "./internal/main.go"
280
281
282
283 func klogRun(t *testing.T, flags []string, stderr io.Writer) {
284 callFlags := []string{"run", klogExampleGoFile}
285 callFlags = append(callFlags, flags...)
286
287 cmd := exec.Command("go", callFlags...)
288 cmd.Stderr = stderr
289 cmd.Env = append(os.Environ(),
290 "KLOG_INFO_LOG="+infoLog,
291 "KLOG_WARNING_LOG="+warningLog,
292 "KLOG_ERROR_LOG="+errorLog,
293 "KLOG_FATAL_LOG="+fatalLog,
294 )
295
296 err := cmd.Run()
297
298 if _, ok := err.(*exec.ExitError); !ok {
299 t.Fatalf("Run failed: %v", err)
300 }
301 }
302
303 var logFileLevels = map[int]string{
304 0: "INFO",
305 1: "WARNING",
306 2: "ERROR",
307 3: "FATAL",
308 }
309
310 func getFileContent(t *testing.T, filePath string) string {
311 content, err := ioutil.ReadFile(filePath)
312 if err != nil {
313 t.Errorf("Could not read file '%s': %v", filePath, err)
314 }
315 return string(content)
316 }
317
318 func assertFileIsAbsent(t *testing.T, filePath string) {
319 if _, err := os.Stat(filePath); !os.IsNotExist(err) {
320 t.Errorf("Expected file '%s' not to exist", filePath)
321 }
322 }
323
324 func checkForLogs(t *testing.T, expected, disallowed res, content, name string) {
325 for _, re := range expected {
326 checkExpected(t, true, name, content, re)
327 }
328 for _, re := range disallowed {
329 checkExpected(t, false, name, content, re)
330 }
331 }
332
333 func checkExpected(t *testing.T, expected bool, where string, haystack string, needle *regexp.Regexp) {
334 found := needle.MatchString(haystack)
335
336 if expected && !found {
337 t.Errorf("Expected to find '%s' in %s", needle, where)
338 }
339 if !expected && found {
340 t.Errorf("Expected not to find '%s' in %s", needle, where)
341 }
342 }
343
344 func withTmpDir(t *testing.T, f func(string)) {
345 tmpDir, err := ioutil.TempDir("", "klog_e2e_")
346 if err != nil {
347 t.Fatalf("Could not create temp directory: %v", err)
348 }
349 defer func() {
350 if err := os.RemoveAll(tmpDir); err != nil {
351 t.Fatalf("Could not remove temp directory '%s': %v", tmpDir, err)
352 }
353 }()
354
355 f(tmpDir)
356 }
357
358
359
360
361 func getLogFilePath(dir, binaryName, levelName string) (string, error) {
362 symlink := filepath.Join(dir, binaryName+"."+levelName)
363 if _, err := os.Stat(symlink); err == nil {
364 return symlink, nil
365 }
366 files, err := ioutil.ReadDir(dir)
367 if err != nil {
368 return "", fmt.Errorf("could not read directory %s: %v", dir, err)
369 }
370 var foundFile string
371 for _, file := range files {
372 if strings.HasPrefix(file.Name(), binaryName) && strings.Contains(file.Name(), levelName) {
373 if foundFile != "" {
374 return "", fmt.Errorf("found multiple matching files")
375 }
376 foundFile = file.Name()
377 }
378 }
379 if foundFile != "" {
380 return filepath.Join(dir, foundFile), nil
381 }
382 return "", fmt.Errorf("file missing from directory")
383 }
384
View as plain text