generated from mrz1836/go-template
-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfuzz_test.go
More file actions
360 lines (304 loc) · 9.97 KB
/
fuzz_test.go
File metadata and controls
360 lines (304 loc) · 9.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
package runner
import (
"context"
"os"
"path/filepath"
"strings"
"testing"
"time"
"github.com/mrz1836/go-pre-commit/internal/config"
)
// FuzzRunnerOptions tests the runner with various option configurations
func FuzzRunnerOptions(f *testing.F) {
// Seed corpus with various option scenarios
f.Add(int(0), false, "fumpt", "") // No parallel, no fail-fast
f.Add(int(1), true, "fumpt,lint", "whitespace") // Single worker, fail-fast
f.Add(int(-1), false, "", "fumpt,lint") // Invalid parallel count
f.Add(int(100), true, "invalid", "") // Too many workers
f.Add(int(2), false, "", "") // Normal case
f.Fuzz(func(t *testing.T, parallel int, failFast bool, onlyChecks, skipChecks string) {
// Create temporary directory with test files
tmpDir := t.TempDir()
testFile := filepath.Join(tmpDir, "test.go")
err := os.WriteFile(testFile, []byte("package main\n\nfunc main() {}\n"), 0o600)
if err != nil {
t.Skip("Failed to create test file")
}
// Create configuration
cfg := &config.Config{
Enabled: true,
Timeout: 30,
}
cfg.Checks.Fumpt = true
cfg.Checks.Lint = false // Disable lint to avoid external dependencies
cfg.Checks.ModTidy = false
cfg.Checks.Whitespace = true
cfg.Checks.EOF = true
// Create runner
runner := New(cfg, tmpDir)
// Parse check lists (handle invalid input gracefully)
var onlyList, skipList []string
if strings.TrimSpace(onlyChecks) != "" {
onlyList = strings.Split(onlyChecks, ",")
}
if strings.TrimSpace(skipChecks) != "" {
skipList = strings.Split(skipChecks, ",")
}
// Create options with fuzzed parameters
opts := Options{
Files: []string{testFile},
OnlyChecks: onlyList,
SkipChecks: skipList,
Parallel: parallel,
FailFast: failFast,
}
// Run should handle any configuration gracefully
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
results, err := runner.Run(ctx, opts)
// Verify runner doesn't crash with invalid configs
if err != nil {
// Some errors are expected for invalid configurations
if strings.Contains(err.Error(), "panic") {
t.Errorf("Runner panicked with options: parallel=%d, failFast=%v, only=%s, skip=%s: %v",
parallel, failFast, onlyChecks, skipChecks, err)
}
}
// If successful, verify results structure
if results != nil {
if results.Passed < 0 || results.Failed < 0 || results.Skipped < 0 {
t.Error("Results contain negative counts")
}
if results.TotalDuration < 0 {
t.Error("Results contain negative duration")
}
}
})
}
// FuzzRunnerWithInvalidFiles tests runner behavior with malformed file paths
func FuzzRunnerWithInvalidFiles(f *testing.F) {
// Seed with various file path scenarios
f.Add("normal.go")
f.Add("")
f.Add("nonexistent.go")
f.Add("../../../etc/passwd")
f.Add("file with spaces.go")
f.Add("unicode🚀file.go")
f.Add("file1.go,file2.go,nonexistent.go")
f.Fuzz(func(t *testing.T, filePathsStr string) {
// Parse comma-separated file paths
var filePaths []string
if strings.TrimSpace(filePathsStr) != "" {
filePaths = strings.Split(filePathsStr, ",")
}
// Skip extremely large file lists
if len(filePaths) > 100 {
t.Skip("Skipping very large file list")
}
// Create configuration
cfg := &config.Config{
Enabled: true,
Timeout: 10,
}
cfg.Checks.Whitespace = true
cfg.Checks.EOF = true
// Create temporary directory
tmpDir := t.TempDir()
runner := New(cfg, tmpDir)
// Process file paths - create valid ones, leave invalid ones as-is
var testFiles []string
for _, filePath := range filePaths {
if filePath == "" || strings.Contains(filePath, "\x00") {
// Add invalid path directly to test error handling
testFiles = append(testFiles, filePath)
continue
}
// Create actual file for valid-looking paths
if !strings.Contains(filePath, "../") && !strings.HasPrefix(filePath, "/") {
cleanPath := filepath.Clean(filepath.Base(filePath))
if cleanPath != "" && cleanPath != "." && cleanPath != ".." {
testFile := filepath.Join(tmpDir, cleanPath)
_ = os.WriteFile(testFile, []byte("test content\n"), 0o600)
testFiles = append(testFiles, testFile)
}
} else {
// Add problematic path to test error handling
testFiles = append(testFiles, filePath)
}
}
// Create options
opts := Options{
Files: testFiles,
Parallel: 1,
FailFast: false,
}
// Run should handle invalid file lists gracefully
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
results, err := runner.Run(ctx, opts)
// Runner should not panic regardless of input
if err != nil && strings.Contains(err.Error(), "panic") {
t.Errorf("Runner panicked with files %v: %v", filePathsStr, err)
}
// If results returned, verify basic structure
if results != nil && results.TotalFiles < 0 {
t.Error("Results reported negative file count")
}
})
}
// FuzzProgressCallback tests progress callback handling with various inputs
func FuzzProgressCallback(f *testing.F) {
// Seed with various callback scenarios
f.Add("fumpt", "starting")
f.Add("", "")
f.Add("very long check name that might cause issues", "status")
f.Add("check\x00name", "status\x00with\x00nulls")
f.Add("unicode🚀check", "unicode🎯status")
f.Fuzz(func(t *testing.T, checkName, status string) {
// Skip very long inputs
if len(checkName) > 1000 || len(status) > 1000 {
t.Skip("Skipping very long inputs")
}
// Create configuration
cfg := &config.Config{
Enabled: true,
Timeout: 5,
}
cfg.Checks.Whitespace = true
// Create temporary file and runner
tmpDir := t.TempDir()
testFile := filepath.Join(tmpDir, "test.txt")
err := os.WriteFile(testFile, []byte("test content\n"), 0o600)
if err != nil {
t.Skip("Failed to create test file")
}
runner := New(cfg, tmpDir)
// Create progress callback that shouldn't panic
callbackCalled := false
progressCallback := func(name, stat string, _ time.Duration) {
callbackCalled = true
// Callback should handle any input without panicking
// Just use the variables to avoid unused warnings
_ = name
_ = stat
}
// Create options with callback
opts := Options{
Files: []string{testFile},
Parallel: 1,
ProgressCallback: progressCallback,
}
// Run with progress callback
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
_, err = runner.Run(ctx, opts)
// Should not panic regardless of callback behavior
if err != nil && strings.Contains(err.Error(), "panic") {
t.Errorf("Runner panicked with progress callback: %v", err)
}
// Use the callback variable to avoid unused variable warning
_ = callbackCalled
})
}
// FuzzRunnerTimeout tests timeout handling with various timeout values
func FuzzRunnerTimeout(f *testing.F) {
// Seed with various timeout scenarios
f.Add(int64(0)) // No timeout
f.Add(int64(1)) // Very short timeout
f.Add(int64(-1)) // Invalid timeout
f.Add(int64(300)) // Normal timeout
f.Add(int64(999999)) // Very long timeout
f.Fuzz(func(t *testing.T, timeoutSeconds int64) {
// Skip extreme values to avoid resource issues
if timeoutSeconds > 3600 || timeoutSeconds < -100 {
t.Skip("Skipping extreme timeout values")
}
// Create configuration with fuzzed timeout
cfg := &config.Config{
Enabled: true,
Timeout: int(timeoutSeconds),
}
cfg.Checks.Whitespace = true
// Create test file
tmpDir := t.TempDir()
testFile := filepath.Join(tmpDir, "test.txt")
err := os.WriteFile(testFile, []byte("content\n"), 0o600)
if err != nil {
t.Skip("Failed to create test file")
}
runner := New(cfg, tmpDir)
// Use reasonable context timeout regardless of config timeout
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
opts := Options{
Files: []string{testFile},
Parallel: 1,
}
// Runner should handle invalid timeout configurations gracefully
results, err := runner.Run(ctx, opts)
// Should not panic with timeout issues
if err != nil && strings.Contains(err.Error(), "panic") {
t.Errorf("Runner panicked with timeout %d: %v", timeoutSeconds, err)
}
// Verify results structure if returned
if results != nil && results.TotalDuration < 0 {
t.Error("Results contain negative total duration")
}
})
}
// FuzzRunnerConcurrency tests concurrent runner execution
func FuzzRunnerConcurrency(f *testing.F) {
// Seed with various concurrency scenarios
f.Add(int(1)) // Single threaded
f.Add(int(2)) // Normal parallel
f.Add(int(0)) // Auto-detect
f.Add(int(-1)) // Invalid
f.Add(int(50)) // High concurrency
f.Fuzz(func(t *testing.T, workers int) {
// Limit workers to reasonable range
if workers > 20 || workers < -5 {
t.Skip("Skipping extreme worker count")
}
// Create configuration
cfg := &config.Config{
Enabled: true,
Timeout: 10,
}
cfg.Performance.ParallelWorkers = workers
cfg.Checks.Whitespace = true
cfg.Checks.EOF = true
// Create multiple test files
tmpDir := t.TempDir()
var testFiles []string
for i := 0; i < 3; i++ {
testFile := filepath.Join(tmpDir, "test"+string(rune(i))+"test.txt")
err := os.WriteFile(testFile, []byte("content\n"), 0o600)
if err != nil {
continue
}
testFiles = append(testFiles, testFile)
}
if len(testFiles) == 0 {
t.Skip("Failed to create test files")
}
runner := New(cfg, tmpDir)
opts := Options{
Files: testFiles,
Parallel: workers,
}
// Test concurrent execution
ctx, cancel := context.WithTimeout(context.Background(), 8*time.Second)
defer cancel()
results, err := runner.Run(ctx, opts)
// Should handle concurrency without panicking
if err != nil && strings.Contains(err.Error(), "panic") {
t.Errorf("Runner panicked with %d workers: %v", workers, err)
}
// Basic result validation
if results != nil {
if results.Passed < 0 || results.Failed < 0 || results.Skipped < 0 {
t.Error("Invalid result counts from concurrent execution")
}
}
})
}