blob: bbee8369f3a9bf309356397d18d39546a022e353 [file] [log] [blame]
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +01001// Copyright 2018 The CUE Authors
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
15package cue
16
17import (
18 "bytes"
19 "testing"
20
21 "cuelang.org/go/cue/errors"
Marcel van Lohuizen7b5d2fd2018-12-11 16:34:56 +010022 "cuelang.org/go/cue/parser"
23 "cuelang.org/go/cue/token"
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +010024)
25
26func TestCompile(t *testing.T) {
27 testCases := []struct {
28 in string
29 out string
30 }{{
31 in: `{
32 foo: 1,
33 }`,
34 out: "<0>{}", // emitted value, but no top-level fields
35 }, {
36 in: `
37 foo: 1
38 `,
39 out: "<0>{foo: 1}",
40 }, {
41 in: `
42 a: true
43 b: 2K
44 c: 4_5
45 d: "abc"
46 e: 3e2 // 3h1m2ss
47 `,
48 out: "<0>{a: true, b: 2000, c: 45, d: \"abc\", e: 3e+2}",
49 }, {
50 in: `
51 a: null
52 b: true
53 c: false
54 `,
55 out: "<0>{a: null, b: true, c: false}",
56 }, {
57 in: "" +
58 `a: "\(4)",
59 b: "one \(a) two \( a + c )",
60 c: "one"`,
61 out: `<0>{a: ""+4+"", b: "one "+<0>.a+" two "+(<0>.a + <0>.c)+"", c: "one"}`,
62 }, {
63 in: "" +
64 `a: """
65 multi
66 """,
67 b: '''
68 hello world
69 goodbye globe
70 welcome back planet
71 '''`,
72 out: `<0>{a: "multi", b: 'hello world\ngoodbye globe\nwelcome back planet'}`,
73 }, {
74 in: "" +
75 `a: """
76 multi \(4)
77 """,
78 b: """
79 hello \("world")
80 goodbye \("globe")
81 welcome back \("planet")
82 """`,
83 out: `<0>{a: "multi "+4+"", b: "hello "+"world"+"\ngoodbye "+"globe"+"\nwelcome back "+"planet"+""}`,
84 }, {
85 in: `
86 a: _
87 b: int
88 c: float
89 d: bool
90 e: duration
91 f: string
92 `,
93 out: "<0>{a: _, b: int, c: float, d: bool, e: duration, f: string}",
94 }, {
95 in: `
96 a: null
97 b: true
98 c: false
99 `,
100 out: "<0>{a: null, b: true, c: false}",
101 }, {
102 in: `
103 null: null
104 true: true
105 false: false
106 `,
107 out: "<0>{null: null, true: true, false: false}",
108 }, {
109 in: `
110 a: 1 + 2
111 b: -2 - 3
112 c: !d
113 d: true
114 `,
115 out: "<0>{a: (1 + 2), b: (-2 - 3), c: !<0>.d, d: true}",
116 }, {
117 in: `
118 l0: 3*[int]
119 l0: [1, 2, 3]
120 l1: (0..5)*[string]
121 l1: ["a", "b"]
122 l2: (0..5)*[{ a: int }]
123 l2: [{a: 1}, {a: 2, b: 3}]
124 l3: (0..10)*[int]
125 l3: [1, 2, 3, ...]
126 l4: [1, 2, ...]
127 l4: [...int]
128 l5: [1, ...int]
129
130 s1: ((0..6)*[int])[2:3]
131 s2: [0,2,3][1:2]
132
133 e0: (2..5)*[{}]
134 e0: [{}]
135 `,
136 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>{}])}`,
137 }, {
138 in: `
139 a: 5 | "a" | true
140 b c: {
141 cc: { ccc: 3 }
142 }
143 d: true
144 `,
145 out: "<0>{a: (5 | \"a\" | true), b: <1>{c: <2>{cc: <3>{ccc: 3}}}, d: true}",
146 }, {
147 in: `
148 a a: { b: a } // referencing ancestor nodes is legal.
149 a b: a.a // do lookup before merging of nodes
150 b: a.a // different node as a.a.b, as first node counts
151 c: a // same node as b, as first node counts
152 d: a["a"]
153 `,
154 out: `<0>{a: (<1>{a: <2>{b: <2>}} & <3>{b: <3>.a}), b: <0>.a.a, c: <0>.a, d: <0>.a["a"]}`,
155 }, {
156 // bunch of aliases
157 in: `
158 a1 = a2
159 a2 = 5
160 b: a1
161 a3 = d
162 c: {
163 d: {
164 r: a3
165 }
166 r: a3
167 }
168 d: { e: 4 }
169 `,
170 out: `<0>{b: 5, c: <1>{d: <2>{r: <0>.d}, r: <0>.d}, d: <3>{e: 4}}`,
171 }, {
172 // aliases with errors
173 in: `
174 e1 = 1
175 e1 = 2
176 e1v: e1
177 e2: "a"
178 e2 = "a"
179 `,
180 out: "cannot have two aliases with the same name in the same scope:\n" +
181 " test:3:3\n" +
182 "cannot have alias and non-alias with the same name:\n" +
183 " test:6:3\n" +
184 "<0>{}",
185 }, {
186 in: `
187 a = b
188 b: {
189 c: a // reference to own root.
190 }
191 `,
192 out: `<0>{b: <1>{c: <0>.b}}`,
193 }, {
194 in: `
195 a: {
196 <name>: { n: name }
197 k: 1
198 }
199 b: {
200 <x>: { x: 0, y: 1 }
201 v: {}
202 }
203 `,
204 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>{}}}`,
205 }, {
206 in: `
Marcel van Lohuizen76b92b52018-12-16 10:47:03 +0100207 a: { "\(k)": v for k, v in b if b.a < k }
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100208 b: {
209 a: 1
210 b: 2
211 c: 3
212 }
213 `,
Marcel van Lohuizen76b92b52018-12-16 10:47:03 +0100214 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}}`,
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100215 }, {
216 in: `
Marcel van Lohuizen76b92b52018-12-16 10:47:03 +0100217 a: { "\(v)": v for k, v in b }
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100218 b: { a: "aa", b: "bb", c: "cc" }
219 `,
Marcel van Lohuizen76b92b52018-12-16 10:47:03 +0100220 out: `<0>{a: { <1>for k, v in <0>.b yield (""+<1>.v+""): <1>.v }, b: <2>{a: "aa", b: "bb", c: "cc"}}`,
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100221 }, {
222 in: `
223 a: [ v for _, v in b ]
224 b: { a: 1, b: 2, c: 3 }
225 `,
226 out: `<0>{a: [ <1>for _, v in <0>.b yield (*nil*): <1>.v ], b: <2>{a: 1, b: 2, c: 3}}`,
227 }, {
228 in: `
229 a: 1..2
230 b: 1..2..3
231 c: "a".."b"
232 d: (2+3)..(4+5)
233 `,
234 out: `<0>{a: (1..2), b: ((1..2)..3), c: ("a".."b"), d: ((2 + 3)..(4 + 5))}`,
235 }}
236 for _, tc := range testCases {
237 t.Run("", func(t *testing.T) {
238 ctx, root, errs := compileFileWithErrors(t, tc.in)
239 buf := &bytes.Buffer{}
240 if len(errs) > 0 {
241 errors.Print(buf, errs)
242 }
243 buf.WriteString(debugStr(ctx, root))
244 got := buf.String()
245 if got != tc.out {
246 t.Errorf("output differs:\ngot %q\nwant %q", got, tc.out)
247 }
248 })
249 }
250}
251
252func TestEmit(t *testing.T) {
253 testCases := []struct {
254 in string
255 out string
256 rw rewriteMode
257 }{{
258 in: `"\(hello), \(world)!"` + `
259 hello: "Hello"
260 world: "World"
261 `,
262 out: `""+<0>.hello+", "+<0>.world+"!"`,
263 rw: evalRaw,
264 }, {
265 in: `"\(hello), \(world)!"` + `
266 hello: "Hello"
267 world: "World"
268 `,
269 out: `"Hello, World!"`,
270 rw: evalPartial,
271 }, {
272 // Ambiguous disjunction must cary over to emit value.
273 in: `baz
274
275 baz: {
276 a: 8000 | 7080
277 a: 7080 | int
278 }`,
279 out: `<0>{a: _|_((8000! | 7080! | 7080):ambiguous disjunction)}`,
280 rw: evalFull,
281 }}
282 for _, tc := range testCases {
283 t.Run("", func(t *testing.T) {
284 ctx, root := compileFile(t, tc.in)
285 v := testResolve(ctx, root.emit, tc.rw)
286 if got := debugStr(ctx, v); got != tc.out {
287 t.Errorf("output differs:\ngot %q\nwant %q", got, tc.out)
288 }
289 })
290 }
291}
Marcel van Lohuizen7b5d2fd2018-12-11 16:34:56 +0100292
293func TestEval(t *testing.T) {
294 testCases := []struct {
295 in string
296 expr string
297 out string
298 }{{
299 in: `
300 hello: "Hello"
301 world: "World"
302 `,
303 expr: `"\(hello), \(world)!"`,
304 out: `"Hello, World!"`,
305 }, {
306 in: `
307 a: { b: 2, c: 3 }
308 z: 1
309 `,
310 expr: `a.b + a.c + z`,
311 out: `6`,
312 }, {
313 in: `
314 a: { b: 2, c: 3 }
315 `,
316 expr: `{ d: a.b + a.c }`,
317 out: `<0>{d: 5}`,
318 }, {
319 in: `
320 a: "Hello World!"
321 `,
322 expr: `strings.ToUpper(a)`,
323 out: `"HELLO WORLD!"`,
324 }, {
325 in: `
326 a: 0x8
327 b: 0x1`,
328 expr: `bits.Or(a, b)`, // package shorthand
329 out: `9`,
330 }, {
331 in: `
332 a: 0x8
333 b: 0x1`,
334 expr: `math.Or(a, b)`,
335 out: `_|_(<0>.Or:undefined field "Or")`,
336 }, {
337 in: `a: 0x8`,
338 expr: `mathematics.Abs(a)`,
339 out: `_|_(reference "mathematics" not found)`,
340 }}
341 for _, tc := range testCases {
342 t.Run("", func(t *testing.T) {
343 ctx, inst, errs := compileInstance(t, tc.in)
344 if errs != nil {
345 t.Fatal(errs)
346 }
347 expr, err := parser.ParseExpr(token.NewFileSet(), "<test>", tc.expr)
348 if err != nil {
349 t.Fatal(err)
350 }
351 evaluated := inst.evalExpr(ctx, expr)
352 v := testResolve(ctx, evaluated, evalFull)
353 if got := debugStr(ctx, v); got != tc.out {
354 t.Errorf("output differs:\ngot %q\nwant %q", got, tc.out)
355 }
356 })
357 }
358}