Upstream itchyny/gojq state of the touched files before 3662ece89c
This commit is contained in:
1 parent
1b0e1db888
commit
d1b69dc316
3 files changed
+926
-54
No files matched your search
@@ -0,0 +1,433 @@
|
||||
package gojq_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/itchyny/gojq"
|
||||
)
|
||||
|
||||
func ExampleCompile() {
|
||||
query, err := gojq.Parse(".[] | .foo")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
code, err := gojq.Compile(query)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
iter := code.Run([]any{
|
||||
nil,
|
||||
"string",
|
||||
42,
|
||||
[]any{"foo"},
|
||||
map[string]any{"foo": 42},
|
||||
})
|
||||
for {
|
||||
v, ok := iter.Next()
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if err, ok := v.(error); ok {
|
||||
fmt.Println(err)
|
||||
continue
|
||||
}
|
||||
fmt.Printf("%#v\n", v)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// <nil>
|
||||
// expected an object but got: string ("string")
|
||||
// expected an object but got: number (42)
|
||||
// expected an object but got: array (["foo"])
|
||||
// 42
|
||||
}
|
||||
|
||||
func ExampleCode_Run() {
|
||||
query, err := gojq.Parse(".foo")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
code, err := gojq.Compile(query)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
input := map[string]any{"foo": 42}
|
||||
iter := code.Run(input)
|
||||
for {
|
||||
v, ok := iter.Next()
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if err, ok := v.(error); ok {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
fmt.Printf("%#v\n", v)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 42
|
||||
}
|
||||
|
||||
func ExampleCode_RunWithContext() {
|
||||
query, err := gojq.Parse("def f: f; f, f")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
code, err := gojq.Compile(query)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
|
||||
defer cancel()
|
||||
iter := code.RunWithContext(ctx, nil)
|
||||
for {
|
||||
v, ok := iter.Next()
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if err, ok := v.(error); ok {
|
||||
fmt.Println(err)
|
||||
continue
|
||||
}
|
||||
_ = v
|
||||
}
|
||||
|
||||
// Output:
|
||||
// context deadline exceeded
|
||||
}
|
||||
|
||||
func TestCodeCompile_OptimizeConstants(t *testing.T) {
|
||||
query, err := gojq.Parse(`[1,{foo:2,"bar":+3},[-4]]`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
code, err := gojq.Compile(query)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
codes := reflect.ValueOf(code).Elem().FieldByName("codes")
|
||||
if got, expected := codes.Len(), 3; expected != got {
|
||||
t.Errorf("expected: %v, got: %v", expected, got)
|
||||
}
|
||||
iter := code.Run(nil)
|
||||
for {
|
||||
got, ok := iter.Next()
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if expected := []any{
|
||||
1, map[string]any{"foo": 2, "bar": 3}, []any{-4},
|
||||
}; !reflect.DeepEqual(got, expected) {
|
||||
t.Errorf("expected: %v, got: %v", expected, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCodeCompile_OptimizeIndexSlice(t *testing.T) {
|
||||
query, err := gojq.Parse(`.foo."bar".["baz"].[-1]."".[0:1]`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
code, err := gojq.Compile(query)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
codes := reflect.ValueOf(code).Elem().FieldByName("codes")
|
||||
if got, expected := codes.Len(), 8; expected != got {
|
||||
t.Errorf("expected: %v, got: %v", expected, got)
|
||||
}
|
||||
iter := code.Run(nil)
|
||||
for {
|
||||
got, ok := iter.Next()
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if got != nil {
|
||||
t.Errorf("expected: %v, got: %v", nil, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCodeCompile_OptimizeIndexSliceAssign(t *testing.T) {
|
||||
query, err := gojq.Parse(`.foo."bar".["baz"].[0]."".[0:1] = [0]`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
code, err := gojq.Compile(query)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
codes := reflect.ValueOf(code).Elem().FieldByName("codes")
|
||||
if got, expected := codes.Len(), 8; expected != got {
|
||||
t.Errorf("expected: %v, got: %v", expected, got)
|
||||
}
|
||||
iter := code.Run(nil)
|
||||
for {
|
||||
got, ok := iter.Next()
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if expected := map[string]any{
|
||||
"foo": map[string]any{
|
||||
"bar": map[string]any{
|
||||
"baz": []any{map[string]any{"": []any{0}}},
|
||||
},
|
||||
},
|
||||
}; !reflect.DeepEqual(got, expected) {
|
||||
t.Errorf("expected: %v, got: %v", expected, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCodeCompile_OptimizeTailRec_While(t *testing.T) {
|
||||
query, err := gojq.Parse("0 | while(. < 10; . + 1)")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
code, err := gojq.Compile(query)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
codes := reflect.ValueOf(code).Elem().FieldByName("codes")
|
||||
if got, expected := codes.Len(), 48; expected != got {
|
||||
t.Errorf("expected: %v, got: %v", expected, got)
|
||||
}
|
||||
op1 := codes.Index(2).Elem().FieldByName("op")
|
||||
op2 := codes.Index(21).Elem().FieldByName("op") // test jump of call _while
|
||||
if got, expected := *(*int)(unsafe.Pointer(op2.UnsafeAddr())),
|
||||
*(*int)(unsafe.Pointer(op1.UnsafeAddr())); expected != got {
|
||||
t.Errorf("expected: %v, got: %v", expected, got)
|
||||
}
|
||||
iter := code.Run(nil)
|
||||
n := 0
|
||||
for {
|
||||
got, ok := iter.Next()
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if !reflect.DeepEqual(got, n) {
|
||||
t.Errorf("expected: %v, got: %v", n, got)
|
||||
}
|
||||
n++
|
||||
}
|
||||
if expected := 10; n != expected {
|
||||
t.Errorf("expected: %v, got: %v", expected, n)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCodeCompile_OptimizeTailRec_CallRec(t *testing.T) {
|
||||
query, err := gojq.Parse(`
|
||||
def f: . as $x | $x, (if $x < 3 then $x + 1 | f else empty end), $x; f
|
||||
`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
code, err := gojq.Compile(query)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
codes := reflect.ValueOf(code).Elem().FieldByName("codes")
|
||||
if got, expected := codes.Len(), 48; expected != got {
|
||||
t.Errorf("expected: %v, got: %v", expected, got)
|
||||
}
|
||||
op1 := codes.Index(39).Elem().FieldByName("op") // callrec f
|
||||
op2 := codes.Index(38).Elem().FieldByName("op") // call _add/2
|
||||
if got, expected := *(*int)(unsafe.Pointer(op2.UnsafeAddr()))+1,
|
||||
*(*int)(unsafe.Pointer(op1.UnsafeAddr())); expected != got {
|
||||
t.Errorf("expected: %v, got: %v", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCodeCompile_OptimizeJumps(t *testing.T) {
|
||||
query, err := gojq.Parse("def f: 1; def g: 2; def h: 3; f")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
code, err := gojq.Compile(query)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
codes := reflect.ValueOf(code).Elem().FieldByName("codes")
|
||||
if got, expected := codes.Len(), 15; expected != got {
|
||||
t.Errorf("expected: %v, got: %v", expected, got)
|
||||
}
|
||||
v := codes.Index(1).Elem().FieldByName("v")
|
||||
if got, expected := *(*any)(unsafe.Pointer(v.UnsafeAddr())), 13; expected != got {
|
||||
t.Errorf("expected: %v, got: %v", expected, got)
|
||||
}
|
||||
iter := code.Run(nil)
|
||||
for {
|
||||
got, ok := iter.Next()
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if expected := 1; !reflect.DeepEqual(got, expected) {
|
||||
t.Errorf("expected: %v, got: %v", expected, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseErrorTokenOffset(t *testing.T) {
|
||||
testCases := []struct {
|
||||
src string
|
||||
offset int
|
||||
}{
|
||||
{src: "^", offset: 1},
|
||||
{src: " ^", offset: 2},
|
||||
{src: " ^ ", offset: 2},
|
||||
{src: "👍", offset: 4},
|
||||
{src: " 👍", offset: 5},
|
||||
{src: " 👍 ", offset: 5},
|
||||
{src: "test👍", offset: 8},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.src, func(t *testing.T) {
|
||||
_, err := gojq.Parse(tc.src)
|
||||
if err == nil {
|
||||
t.Fatal("expected: error")
|
||||
}
|
||||
var pe *gojq.ParseError
|
||||
if !errors.As(err, &pe) {
|
||||
t.Fatalf("expected: *gojq.ParseError, got %v", err)
|
||||
}
|
||||
if pe.Offset != tc.offset {
|
||||
t.Fatalf("expected: %v, got %v", tc.offset, pe.Offset)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCodeRun_Race(t *testing.T) {
|
||||
query, err := gojq.Parse("range(10)")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
code, err := gojq.Compile(query)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var wg sync.WaitGroup
|
||||
for range 10 {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
iter := code.Run(nil)
|
||||
n := 0
|
||||
for {
|
||||
got, ok := iter.Next()
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if got != n {
|
||||
t.Errorf("expected: %v, got: %v", n, got)
|
||||
}
|
||||
n++
|
||||
}
|
||||
if expected := 10; n != expected {
|
||||
t.Errorf("expected: %v, got: %v", expected, n)
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestCodeRun_RaceRegexp(t *testing.T) {
|
||||
query, err := gojq.Parse(`
|
||||
"abcdefg" | test("cd"), split("e") | . == true or length > 0
|
||||
`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
code, err := gojq.Compile(query)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var wg sync.WaitGroup
|
||||
for range 10 {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
iter := code.Run(nil)
|
||||
for {
|
||||
v, ok := iter.Next()
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if err, ok := v.(error); ok {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if v != true {
|
||||
t.Errorf("expected: true, got: %v", v)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestCodeRun_RaceInputIter(t *testing.T) {
|
||||
const n = 10
|
||||
values := make([]int, n)
|
||||
for i := range values {
|
||||
values[i] = i
|
||||
}
|
||||
query, err := gojq.Parse("input")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
code, err := gojq.Compile(query, gojq.WithInputIter(gojq.NewIter(values...)))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var seen sync.Map
|
||||
var wg sync.WaitGroup
|
||||
for range n {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
v, ok := code.Run(nil).Next()
|
||||
if !ok {
|
||||
t.Error("expected a value")
|
||||
return
|
||||
}
|
||||
if _, loaded := seen.LoadOrStore(v, struct{}{}); loaded {
|
||||
t.Errorf("input %v was consumed more than once", v)
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
for i := range n {
|
||||
if _, ok := seen.Load(i); !ok {
|
||||
t.Errorf("expected input %d to be consumed", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCompile(b *testing.B) {
|
||||
cnt, err := os.ReadFile("builtin.jq")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
query, err := gojq.Parse(string(cnt) + ".")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
for b.Loop() {
|
||||
_, err := gojq.Compile(
|
||||
query,
|
||||
gojq.WithInputIter(gojq.NewIter[any]()),
|
||||
)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user