Skip to content

Commit c8a4f0a

Browse files
Implement randomized schema test (#227)
Resolves #183 This adds a new TestRandomizedSchema that generates a schema using random number generator, then compiles and runs the tests for the schema. The new test revealed a number of bugs. I fixed a few of the smaller ones. There are more bugs, but I don't want to overload this PR. For now TestRandomizedSchema does not fail the build when it finds a buggy schema, but merely logs it. After this PR is merged I will fix every failing schema one by one.
1 parent aafb60f commit c8a4f0a

File tree

25 files changed

+636
-95
lines changed

25 files changed

+636
-95
lines changed

examples/ints/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/splunk/stef/examples/ints
22

3-
go 1.23.2
3+
go 1.24.0
44

55
require (
66
github.com/splunk/stef/go/pkg v0.0.8

examples/ints/internal/ints/record.go

Lines changed: 4 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/jsonl/internal/jsonstef/jsonobject.go

Lines changed: 4 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/profile/internal/profile/labels.go

Lines changed: 4 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

go/otel/oteltef/attributes.go

Lines changed: 4 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

go/otel/oteltef/envelopeattributes.go

Lines changed: 4 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

go/otel/oteltef/keyvaluelist.go

Lines changed: 4 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

go/pkg/schema/schema.go

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"errors"
55
"fmt"
66
"sort"
7+
"strings"
78
)
89

910
// Schema is a STEF schema description, serializable in JSON format.
@@ -606,6 +607,130 @@ func (d *Schema) markReachableFromFieldType(
606607
}
607608
}
608609

610+
// PrettyPrint outputs the schema in SDL format.
611+
func (d *Schema) PrettyPrint() string {
612+
var out []string
613+
614+
// Print package declaration
615+
out = append(out, fmt.Sprintf("package %s", strings.Join(d.PackageName, ".")))
616+
617+
// Print enums
618+
for _, enum := range sortedList(d.Enums) {
619+
out = append(out, prettyPrintEnum(enum))
620+
}
621+
622+
// Print multimaps
623+
for _, mm := range sortedList(d.Multimaps) {
624+
out = append(out, prettyPrintMultimap(mm))
625+
}
626+
627+
// Print structs and oneofs
628+
for _, s := range sortedList(d.Structs) {
629+
out = append(out, prettyPrintStruct(s))
630+
}
631+
632+
return strings.Join(out, "\n\n")
633+
}
634+
635+
func sortedList[T any](m map[string]*T) []*T {
636+
var names []string
637+
for k := range m {
638+
names = append(names, k)
639+
}
640+
sort.Strings(names)
641+
var out []*T
642+
for _, n := range names {
643+
out = append(out, m[n])
644+
}
645+
return out
646+
}
647+
648+
func prettyPrintEnum(e *Enum) string {
649+
out := fmt.Sprintf("enum %s {", e.Name)
650+
for _, f := range e.Fields {
651+
out += fmt.Sprintf("\n %s = %d", f.Name, f.Value)
652+
}
653+
out += "\n}"
654+
return out
655+
}
656+
657+
func prettyPrintMultimap(m *Multimap) string {
658+
out := fmt.Sprintf("multimap %s {\n", m.Name)
659+
out += " key " + prettyPrintFieldType(&m.Key.Type)
660+
if dict := m.Key.Type.DictName; dict != "" {
661+
out += fmt.Sprintf(" dict(%s)", dict)
662+
}
663+
out += "\n"
664+
out += " value " + prettyPrintFieldType(&m.Value.Type)
665+
if dict := m.Value.Type.DictName; dict != "" {
666+
out += fmt.Sprintf(" dict(%s)", dict)
667+
}
668+
out += "\n}"
669+
return out
670+
}
671+
672+
func prettyPrintStruct(s *Struct) string {
673+
var out string
674+
if s.OneOf {
675+
out = fmt.Sprintf("oneof %s {", s.Name)
676+
} else {
677+
out = fmt.Sprintf("struct %s", s.Name)
678+
if s.DictName != "" {
679+
out += fmt.Sprintf(" dict(%s)", s.DictName)
680+
}
681+
if s.IsRoot {
682+
out += " root"
683+
}
684+
out += " {"
685+
}
686+
for _, f := range s.Fields {
687+
out += "\n " + prettyPrintStructField(f)
688+
}
689+
out += "\n}"
690+
return out
691+
}
692+
693+
func prettyPrintStructField(f *StructField) string {
694+
ft := prettyPrintFieldType(&f.FieldType)
695+
out := fmt.Sprintf("%s %s", f.Name, ft)
696+
if f.DictName != "" {
697+
out += fmt.Sprintf(" dict(%s)", f.DictName)
698+
}
699+
if f.Optional {
700+
out += " optional"
701+
}
702+
return out
703+
}
704+
705+
func prettyPrintFieldType(ft *FieldType) string {
706+
switch {
707+
case ft.Primitive != nil:
708+
switch ft.Primitive.Type {
709+
case PrimitiveTypeInt64:
710+
return "int64"
711+
case PrimitiveTypeUint64:
712+
return "uint64"
713+
case PrimitiveTypeFloat64:
714+
return "float64"
715+
case PrimitiveTypeBool:
716+
return "bool"
717+
case PrimitiveTypeString:
718+
return "string"
719+
case PrimitiveTypeBytes:
720+
return "bytes"
721+
}
722+
case ft.Array != nil:
723+
return "[]" + prettyPrintFieldType(&ft.Array.ElemType)
724+
case ft.Struct != "":
725+
return ft.Struct
726+
case ft.MultiMap != "":
727+
return ft.MultiMap
728+
case ft.Enum != "":
729+
return ft.Enum
730+
}
731+
return "unknown"
732+
}
733+
609734
type Struct struct {
610735
Name string `json:"name,omitempty"`
611736
OneOf bool `json:"oneof,omitempty"`
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
package schema
2+
3+
import (
4+
"strings"
5+
"testing"
6+
7+
"github.com/stretchr/testify/require"
8+
)
9+
10+
func TestPrettyPrint_SimpleStruct(t *testing.T) {
11+
schema := &Schema{
12+
PackageName: []string{"com", "example", "test"},
13+
Structs: map[string]*Struct{
14+
"Person": {
15+
Name: "Person",
16+
Fields: []*StructField{
17+
{Name: "Name", FieldType: FieldType{Primitive: &PrimitiveType{Type: PrimitiveTypeString}}},
18+
{Name: "Age", FieldType: FieldType{Primitive: &PrimitiveType{Type: PrimitiveTypeUint64}}},
19+
},
20+
},
21+
},
22+
}
23+
24+
expected := `package com.example.test
25+
26+
struct Person {
27+
Name string
28+
Age uint64
29+
}`
30+
actual := strings.TrimSpace(schema.PrettyPrint())
31+
require.Equal(t, expected, actual)
32+
}
33+
34+
func TestPrettyPrint_EnumAndMultimap(t *testing.T) {
35+
schema := &Schema{
36+
PackageName: []string{"com", "example", "test"},
37+
Enums: map[string]*Enum{
38+
"MetricType": {
39+
Name: "MetricType",
40+
Fields: []EnumField{
41+
{Name: "Gauge", Value: 0},
42+
{Name: "Counter", Value: 1},
43+
},
44+
},
45+
},
46+
Multimaps: map[string]*Multimap{
47+
"Labels": {
48+
Name: "Labels",
49+
Key: MultimapField{
50+
Type: FieldType{
51+
Primitive: &PrimitiveType{Type: PrimitiveTypeString}, DictName: "LabelKeys",
52+
},
53+
},
54+
Value: MultimapField{
55+
Type: FieldType{
56+
Primitive: &PrimitiveType{Type: PrimitiveTypeString}, DictName: "LabelValues",
57+
},
58+
},
59+
},
60+
},
61+
}
62+
63+
expected := `package com.example.test
64+
65+
enum MetricType {
66+
Gauge = 0
67+
Counter = 1
68+
}
69+
70+
multimap Labels {
71+
key string dict(LabelKeys)
72+
value string dict(LabelValues)
73+
}`
74+
actual := strings.TrimSpace(schema.PrettyPrint())
75+
require.Equal(t, expected, actual)
76+
}
77+
78+
func TestPrettyPrint_OneofAndArray(t *testing.T) {
79+
schema := &Schema{
80+
PackageName: []string{"example"},
81+
Structs: map[string]*Struct{
82+
"JsonValue": {
83+
Name: "JsonValue",
84+
OneOf: true,
85+
Fields: []*StructField{
86+
{Name: "String", FieldType: FieldType{Primitive: &PrimitiveType{Type: PrimitiveTypeString}}},
87+
{Name: "Number", FieldType: FieldType{Primitive: &PrimitiveType{Type: PrimitiveTypeFloat64}}},
88+
{Name: "Array", FieldType: FieldType{Array: &ArrayType{ElemType: FieldType{Struct: "JsonValue"}}}},
89+
},
90+
},
91+
},
92+
}
93+
expected := `package example
94+
95+
oneof JsonValue {
96+
String string
97+
Number float64
98+
Array []JsonValue
99+
}`
100+
actual := strings.TrimSpace(schema.PrettyPrint())
101+
require.Equal(t, expected, actual)
102+
}
103+
104+
func TestPrettyPrint_OptionalAndDict(t *testing.T) {
105+
schema := &Schema{
106+
PackageName: []string{"example"},
107+
Structs: map[string]*Struct{
108+
"User": {
109+
Name: "User",
110+
Fields: []*StructField{
111+
{Name: "Name", FieldType: FieldType{Primitive: &PrimitiveType{Type: PrimitiveTypeString}}},
112+
{
113+
Name: "Email",
114+
FieldType: FieldType{Primitive: &PrimitiveType{Type: PrimitiveTypeString}, DictName: "Emails"},
115+
Optional: true,
116+
},
117+
},
118+
},
119+
},
120+
}
121+
expected := `package example
122+
123+
struct User {
124+
Name string
125+
Email string dict(Emails) optional
126+
}`
127+
actual := strings.TrimSpace(schema.PrettyPrint())
128+
require.Equal(t, expected, actual)
129+
}

java/src/main/java/com/example/oteltef/Attributes.java

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -135,21 +135,16 @@ public void append(StringValue k, AnyValue v) {
135135
elems[elemsLen-1] = elem;
136136
}
137137

138+
138139
// setKey sets the key of the element at index i.
139140
public void setKey(int i, StringValue k) {
140141
if (!Types.StringEqual(elems[i].key, k)) {
141142
elems[i].key = k;
142143
modifiedElems.markKeyModified(i);
143144
}
144145
}
145-
146-
// setValue sets the value of the element at index i.
147-
public void setValue(int i, AnyValue v) {
148-
if (!elems[i].value.equals(v)) {
149-
elems[i].value = v;
150-
modifiedElems.markValModified(i);
151-
}
152-
}
146+
147+
153148

154149
// byteSize returns approximate memory usage in bytes. Used to calculate
155150
// memory used by dictionaries.

0 commit comments

Comments
 (0)