blob: 4fe6367418ec3f978353ef88cbafd31f08f3eac3 [file] [log] [blame]
// Copyright 2018 The CUE Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cue
import (
"bytes"
"testing"
"cuelang.org/go/cue/errors"
)
func TestCompile(t *testing.T) {
testCases := []struct {
in string
out string
}{{
in: `{
foo: 1,
}`,
out: "<0>{}", // emitted value, but no top-level fields
}, {
in: `
foo: 1
`,
out: "<0>{foo: 1}",
}, {
in: `
a: true
b: 2K
c: 4_5
d: "abc"
e: 3e2 // 3h1m2ss
`,
out: "<0>{a: true, b: 2000, c: 45, d: \"abc\", e: 3e+2}",
}, {
in: `
a: null
b: true
c: false
`,
out: "<0>{a: null, b: true, c: false}",
}, {
in: "" +
`a: "\(4)",
b: "one \(a) two \( a + c )",
c: "one"`,
out: `<0>{a: ""+4+"", b: "one "+<0>.a+" two "+(<0>.a + <0>.c)+"", c: "one"}`,
}, {
in: "" +
`a: """
multi
""",
b: '''
hello world
goodbye globe
welcome back planet
'''`,
out: `<0>{a: "multi", b: 'hello world\ngoodbye globe\nwelcome back planet'}`,
}, {
in: "" +
`a: """
multi \(4)
""",
b: """
hello \("world")
goodbye \("globe")
welcome back \("planet")
"""`,
out: `<0>{a: "multi "+4+"", b: "hello "+"world"+"\ngoodbye "+"globe"+"\nwelcome back "+"planet"+""}`,
}, {
in: `
a: _
b: int
c: float
d: bool
e: duration
f: string
`,
out: "<0>{a: _, b: int, c: float, d: bool, e: duration, f: string}",
}, {
in: `
a: null
b: true
c: false
`,
out: "<0>{a: null, b: true, c: false}",
}, {
in: `
null: null
true: true
false: false
`,
out: "<0>{null: null, true: true, false: false}",
}, {
in: `
a: 1 + 2
b: -2 - 3
c: !d
d: true
`,
out: "<0>{a: (1 + 2), b: (-2 - 3), c: !<0>.d, d: true}",
}, {
in: `
l0: 3*[int]
l0: [1, 2, 3]
l1: (0..5)*[string]
l1: ["a", "b"]
l2: (0..5)*[{ a: int }]
l2: [{a: 1}, {a: 2, b: 3}]
l3: (0..10)*[int]
l3: [1, 2, 3, ...]
l4: [1, 2, ...]
l4: [...int]
l5: [1, ...int]
s1: ((0..6)*[int])[2:3]
s2: [0,2,3][1:2]
e0: (2..5)*[{}]
e0: [{}]
`,
out: `<0>{l0: ((3 * [int]) & [1,2,3]), l1: (((0..5) * [string]) & ["a","b"]), l2: (((0..5) * [<1>{a: int}]) & [<2>{a: 1},<3>{a: 2, b: 3}]), l3: (((0..10) * [int]) & [1,2,3, ...]), l4: ([1,2, ...] & [, ...int]), l5: [1, ...int], s1: ((0..6) * [int])[2:3], s2: [0,2,3][1:2], e0: (((2..5) * [<4>{}]) & [<5>{}])}`,
}, {
in: `
a: 5 | "a" | true
b c: {
cc: { ccc: 3 }
}
d: true
`,
out: "<0>{a: (5 | \"a\" | true), b: <1>{c: <2>{cc: <3>{ccc: 3}}}, d: true}",
}, {
in: `
a a: { b: a } // referencing ancestor nodes is legal.
a b: a.a // do lookup before merging of nodes
b: a.a // different node as a.a.b, as first node counts
c: a // same node as b, as first node counts
d: a["a"]
`,
out: `<0>{a: (<1>{a: <2>{b: <2>}} & <3>{b: <3>.a}), b: <0>.a.a, c: <0>.a, d: <0>.a["a"]}`,
}, {
// bunch of aliases
in: `
a1 = a2
a2 = 5
b: a1
a3 = d
c: {
d: {
r: a3
}
r: a3
}
d: { e: 4 }
`,
out: `<0>{b: 5, c: <1>{d: <2>{r: <0>.d}, r: <0>.d}, d: <3>{e: 4}}`,
}, {
// aliases with errors
in: `
e1 = 1
e1 = 2
e1v: e1
e2: "a"
e2 = "a"
`,
out: "cannot have two aliases with the same name in the same scope:\n" +
" test:3:3\n" +
"cannot have alias and non-alias with the same name:\n" +
" test:6:3\n" +
"<0>{}",
}, {
in: `
a = b
b: {
c: a // reference to own root.
}
`,
out: `<0>{b: <1>{c: <0>.b}}`,
}, {
in: `
a: {
<name>: { n: name }
k: 1
}
b: {
<x>: { x: 0, y: 1 }
v: {}
}
`,
out: `<0>{a: <1>{<>: <2>(name: string)-><3>{n: <2>.name}, k: 1}, b: <4>{<>: <5>(x: string)-><6>{x: 0, y: 1}, v: <7>{}}}`,
}, {
in: `
a: { [k]: v for k, v in b if b.a < k }
b: {
a: 1
b: 2
c: 3
}
`,
out: `<0>{a: { <1>for k, v in <0>.b if (<0>.b.a < <1>.k) yield (<1>.k): <1>.v }, b: <2>{a: 1, b: 2, c: 3}}`,
}, {
in: `
a: { [v]: v for k, v in b }
b: { a: "aa", b: "bb", c: "cc" }
`,
out: `<0>{a: { <1>for k, v in <0>.b yield (<1>.v): <1>.v }, b: <2>{a: "aa", b: "bb", c: "cc"}}`,
}, {
in: `
a: [ v for _, v in b ]
b: { a: 1, b: 2, c: 3 }
`,
out: `<0>{a: [ <1>for _, v in <0>.b yield (*nil*): <1>.v ], b: <2>{a: 1, b: 2, c: 3}}`,
}, {
in: `
a: 1..2
b: 1..2..3
c: "a".."b"
d: (2+3)..(4+5)
`,
out: `<0>{a: (1..2), b: ((1..2)..3), c: ("a".."b"), d: ((2 + 3)..(4 + 5))}`,
}}
for _, tc := range testCases {
t.Run("", func(t *testing.T) {
ctx, root, errs := compileFileWithErrors(t, tc.in)
buf := &bytes.Buffer{}
if len(errs) > 0 {
errors.Print(buf, errs)
}
buf.WriteString(debugStr(ctx, root))
got := buf.String()
if got != tc.out {
t.Errorf("output differs:\ngot %q\nwant %q", got, tc.out)
}
})
}
}
func TestEmit(t *testing.T) {
testCases := []struct {
in string
out string
rw rewriteMode
}{{
in: `"\(hello), \(world)!"` + `
hello: "Hello"
world: "World"
`,
out: `""+<0>.hello+", "+<0>.world+"!"`,
rw: evalRaw,
}, {
in: `"\(hello), \(world)!"` + `
hello: "Hello"
world: "World"
`,
out: `"Hello, World!"`,
rw: evalPartial,
}, {
// Ambiguous disjunction must cary over to emit value.
in: `baz
baz: {
a: 8000 | 7080
a: 7080 | int
}`,
out: `<0>{a: _|_((8000! | 7080! | 7080):ambiguous disjunction)}`,
rw: evalFull,
}}
for _, tc := range testCases {
t.Run("", func(t *testing.T) {
ctx, root := compileFile(t, tc.in)
v := testResolve(ctx, root.emit, tc.rw)
if got := debugStr(ctx, v); got != tc.out {
t.Errorf("output differs:\ngot %q\nwant %q", got, tc.out)
}
})
}
}