-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcorpus_test.go
More file actions
132 lines (117 loc) · 3.56 KB
/
Copy pathcorpus_test.go
File metadata and controls
132 lines (117 loc) · 3.56 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
package eql
import (
"encoding/json"
"os"
"testing"
)
// QueryEntry is one query in the corpus file.
type QueryEntry struct {
Source string `json:"source"`
Name string `json:"name"`
Query string `json:"query"`
// Valid marks whether the query is expected to parse without errors.
// Absent (false) means "unknown / adversarial"; such entries are only
// checked for non-panic and well-formedness.
Valid bool `json:"valid,omitempty"`
}
// TestCorpus runs the parser over a corpus of queries and reports a breakdown.
// Queries flagged Valid must parse cleanly; the rest must at least not panic
// and must produce well-formed conditions.
func TestCorpus(t *testing.T) {
const corpusPath = "testdata/corpus.json"
data, err := os.ReadFile(corpusPath)
if err != nil {
if os.IsNotExist(err) {
t.Skip("corpus not available at testdata/corpus.json")
}
t.Fatalf("read corpus: %v", err)
}
var queries []QueryEntry
if err := json.Unmarshal(data, &queries); err != nil {
t.Fatalf("parse corpus: %v", err)
}
t.Logf("loaded %d queries", len(queries))
var success, partial, noConditions, failed, panics int
var validTotal, validClean int
for _, q := range queries {
func() {
defer func() {
if r := recover(); r != nil {
panics++
t.Errorf("PANIC on %q: %v", q.Name, r)
}
}()
res := ExtractConditions(q.Query)
for i, c := range res.Conditions {
if c.Field == "" || c.Operator == "" {
t.Errorf("%q condition %d malformed: %+v", q.Name, i, c)
}
}
switch {
case len(res.Conditions) > 0 && len(res.Errors) == 0:
success++
case len(res.Conditions) > 0:
partial++
case len(res.Errors) > 0:
failed++
default:
noConditions++
}
if q.Valid {
validTotal++
if len(res.Errors) == 0 {
validClean++
} else {
t.Errorf("query marked valid failed to parse: %q — %v", q.Name, res.Errors)
}
}
}()
}
total := len(queries)
t.Logf("results: success=%d partial=%d no_conditions=%d failed=%d panics=%d (total=%d)",
success, partial, noConditions, failed, panics, total)
// Correctness gate: every query marked valid must parse cleanly.
// (Adversarial queries are expected to fail and are not counted here.)
if validTotal > 0 {
rate := float64(validClean) * 100 / float64(validTotal)
t.Logf("valid-query clean-parse rate: %.1f%% (%d/%d)", rate, validClean, validTotal)
if validClean != validTotal {
t.Errorf("%d/%d valid queries failed to parse cleanly", validTotal-validClean, validTotal)
}
}
if panics > 0 {
t.Errorf("parser panicked %d times", panics)
}
}
// TestGeneratedCorpus performs differential testing against a corpus produced
// by cmd/generated-corpus, where each query has a known set of expected
// conditions.
func TestGeneratedCorpus(t *testing.T) {
const path = "testdata/generated/eql_generated_corpus.json"
skipIfLFSPointer(t, path)
data, err := os.ReadFile(path)
if err != nil {
if os.IsNotExist(err) {
t.Skip("generated corpus not available; run 'make corpus'")
}
t.Fatalf("read generated corpus: %v", err)
}
var cases []GeneratedCase
if err := json.Unmarshal(data, &cases); err != nil {
t.Fatalf("parse generated corpus: %v", err)
}
t.Logf("loaded %d generated cases", len(cases))
var matched, mismatched int
for _, gc := range cases {
res := ExtractConditions(gc.Query)
if diff := diffConditions(gc.Expected, res.Conditions); diff != "" {
mismatched++
if mismatched <= 20 {
t.Errorf("case %d (%s): %s", gc.ID, gc.Query, diff)
}
} else {
matched++
}
}
t.Logf("generated: matched=%d mismatched=%d", matched, mismatched)
}