Skip to content

Commit 22567f7

Browse files
add test to parse to randomly parse random values and make config for rand
1 parent 4c34e6d commit 22567f7

File tree

5 files changed

+137
-27
lines changed

5 files changed

+137
-27
lines changed

json/parse/rand_test.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import (
2121
"github.com/katydid/parser-go-json/json/rand"
2222
)
2323

24-
func TestRandomParse(t *testing.T) {
24+
func TestParseRandomValues(t *testing.T) {
2525
r := rand.NewRand()
2626
values := rand.Values(r, 100)
2727
for _, value := range values {
@@ -34,3 +34,17 @@ func TestRandomParse(t *testing.T) {
3434
})
3535
}
3636
}
37+
38+
func TestRandomlyParseRandomValues(t *testing.T) {
39+
r := rand.NewRand()
40+
values := rand.Values(r, 100)
41+
for _, value := range values {
42+
name := testrun.Name(value)
43+
t.Run(name, func(t *testing.T) {
44+
tokenizer := NewParser([]byte(value))
45+
if err := randWalk(r, tokenizer); err != nil {
46+
t.Fatalf("expected EOF, but got %v", err)
47+
}
48+
})
49+
}
50+
}

json/parse/walk_test.go

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ package parse
1717
import (
1818
"errors"
1919
"io"
20+
21+
"github.com/katydid/parser-go-json/json/rand"
2022
)
2123

2224
var errUnknownToken = errors.New("unknown token")
@@ -25,44 +27,72 @@ var errExpectedBool = errors.New("expected bool")
2527

2628
var errExpectedString = errors.New("expected string")
2729

28-
func walkValue(t Parser, kind Kind) error {
29-
if _, err := t.Bool(); err == nil {
30+
func walkValue(p Parser, kind Kind) error {
31+
if _, err := p.Bool(); err == nil {
3032
return nil
3133
}
3234
if kind == BoolKind {
3335
return errExpectedBool
3436
}
35-
if _, err := t.Int(); err == nil {
37+
if _, err := p.Int(); err == nil {
3638
return nil
3739
}
38-
if _, err := t.Uint(); err == nil {
40+
if _, err := p.Uint(); err == nil {
3941
return nil
4042
}
41-
if _, err := t.Double(); err == nil {
43+
if _, err := p.Double(); err == nil {
4244
return nil
4345
}
44-
if _, err := t.String(); err == nil {
46+
if _, err := p.String(); err == nil {
4547
return nil
4648
}
4749
if kind == StringKind {
4850
return errExpectedString
4951
}
50-
if _, err := t.Bytes(); err == nil {
52+
if _, err := p.Bytes(); err == nil {
5153
return nil
5254
}
5355
return errUnknownToken
5456
}
5557

56-
func walk(t Parser) error {
57-
kind, err := t.Next()
58+
func walk(p Parser) error {
59+
kind, err := p.Next()
60+
for err == nil {
61+
switch kind {
62+
case NullKind, BoolKind, NumberKind, StringKind:
63+
if err := walkValue(p, kind); err != nil {
64+
return err
65+
}
66+
}
67+
kind, err = p.Next()
68+
}
69+
if err != io.EOF {
70+
return err
71+
}
72+
return nil
73+
}
74+
75+
func randNext(r rand.Rand, p Parser) (Kind, error) {
76+
skip := r.Intn(2) == 0
77+
for skip {
78+
if err := p.Skip(); err != nil {
79+
return UnknownKind, err
80+
}
81+
skip = r.Intn(2) == 0
82+
}
83+
return p.Next()
84+
}
85+
86+
func randWalk(r rand.Rand, p Parser) error {
87+
kind, err := p.Next()
5888
for err == nil {
5989
switch kind {
6090
case NullKind, BoolKind, NumberKind, StringKind:
61-
if err := walkValue(t, kind); err != nil {
91+
if err := walkValue(p, kind); err != nil {
6292
return err
6393
}
6494
}
65-
kind, err = t.Next()
95+
kind, err = randNext(r, p)
6696
}
6797
if err != io.EOF {
6898
return err

json/rand/config.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright 2025 Walter Schulze
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package rand
16+
17+
type config struct {
18+
// MaxLevels is the maximum number of objects/arrays to generate down. It makes sure the algorithm terminates.
19+
maxLevels int
20+
}
21+
22+
type Option func(*config)
23+
24+
func newConfig(opts ...Option) *config {
25+
// default Config
26+
c := &config{
27+
maxLevels: 5,
28+
}
29+
// apply options
30+
for _, o := range opts {
31+
o(c)
32+
}
33+
return c
34+
}
35+
36+
func WithMaxLevel(maxLevel int) Option {
37+
return func(c *config) {
38+
c.maxLevels = maxLevel
39+
}
40+
}

json/rand/many.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ func values(r Rand, atleast int) (map[byte]int, []string) {
3131
kinds := allKinds()
3232
ss := []string{}
3333
for !(eachKindHasNonZero(kinds) && len(ss) >= atleast) {
34-
s := Value(r, 5)
34+
s := Value(r)
3535
ss = append(ss, s)
3636
kinds[whichKind(s)] += 1
3737
}

json/rand/rand.go

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,16 @@ import (
2020
)
2121

2222
// Value returns a string representing random json value.
23+
func Value(r Rand, opts ...Option) string {
24+
c := newConfig(opts...)
25+
return randValue(r, c)
26+
}
27+
2328
// value BNF:
2429
// value := object | array | string | number | "true" | "false" | "null"
25-
func Value(r Rand, level int) string {
30+
func randValue(r Rand, c *config) string {
2631
maxN := 7
27-
if level <= 0 {
32+
if c.maxLevels <= 0 {
2833
// do not generate arrays or objects,
2934
// since we have generated a deep enough structure and
3035
// we do not want to endlessly recurse.
@@ -38,59 +43,75 @@ func Value(r Rand, level int) string {
3843
case 2:
3944
return "true"
4045
case 3:
41-
return Number(r)
46+
return randNumber(r, c)
4247
case 4:
43-
return String(r)
48+
return randString(r, c)
4449
case 5:
45-
return Array(r, level-1)
50+
c.maxLevels = c.maxLevels - 1
51+
return randArray(r, c)
4652
case 6:
47-
return Object(r, level-1)
53+
return randObject(r, c)
4854
}
4955
panic("unreachable")
5056
}
5157

5258
// Object returns a string that represents a random JSON object.
59+
func Object(r Rand, opts ...Option) string {
60+
c := newConfig(opts...)
61+
return randObject(r, c)
62+
}
63+
5364
// object BNF:
5465
// object := '{' ws '}' | '{' members '}'
5566
// members := member | member ',' members
5667
// member := ws string ws ':' element
57-
func Object(r Rand, level int) string {
68+
func randObject(r Rand, c *config) string {
5869
l := r.Intn(10)
5970
if l == 0 {
6071
return "{" + randWs(r) + "}"
6172
}
6273
ss := make([]string, l)
6374
for i := 0; i < l; i++ {
64-
ss[i] = randWs(r) + String(r) + randWs(r) + ":" + randElement(r, level)
75+
ss[i] = randWs(r) + String(r) + randWs(r) + ":" + randElement(r, c)
6576
}
6677
return "{" + strings.Join(ss, ",") + "}"
6778
}
6879

6980
// Array returns a string that represents a random JSON array.
81+
func Array(r Rand, opts ...Option) string {
82+
c := newConfig(opts...)
83+
return randArray(r, c)
84+
}
85+
7086
// array := '[' ws ']' | '[' elements ']'
7187
// elements := element | element ',' elements
72-
func Array(r Rand, level int) string {
88+
func randArray(r Rand, c *config) string {
7389
l := r.Intn(10)
7490
if l == 0 {
7591
return "[" + randWs(r) + "]"
7692
}
7793
ss := make([]string, l)
7894
for i := 0; i < l; i++ {
79-
ss[i] = randElement(r, level)
95+
ss[i] = randElement(r, c)
8096
}
8197
return "[" + strings.Join(ss, ",") + "]"
8298
}
8399

84100
// element := ws value ws
85-
func randElement(r Rand, level int) string {
86-
return randWs(r) + Value(r, level) + randWs(r)
101+
func randElement(r Rand, c *config) string {
102+
return randWs(r) + randValue(r, c) + randWs(r)
87103
}
88104

89105
// String returns a string that represents a random JSON string.
106+
func String(r Rand, opts ...Option) string {
107+
c := newConfig(opts...)
108+
return randString(r, c)
109+
}
110+
90111
// String BNF:
91112
// string := '"' characters '"'
92113
// characters := "" | character characters
93-
func String(r Rand) string {
114+
func randString(r Rand, c *config) string {
94115
ss := make([]string, int(r.Intn(100)))
95116
for i := range ss {
96117
ss[i] = randChar(r)
@@ -143,9 +164,14 @@ func randEscape(r Rand) string {
143164
}
144165

145166
// Number returns a string that represents a random JSON number.
167+
func Number(r Rand, opts ...Option) string {
168+
c := newConfig(opts...)
169+
return randNumber(r, c)
170+
}
171+
146172
// number BNF:
147173
// number := integer fraction exponent
148-
func Number(r Rand) string {
174+
func randNumber(r Rand, c *config) string {
149175
// Sometimes generate an edge case
150176
if r.Intn(100) == 0 {
151177
switch r.Intn(9) {

0 commit comments

Comments
 (0)