blob: 91ec603942df7b4df066e6844fbcdcd1c706b643 [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 }, {
Marcel van Lohuizen7d0797b2019-02-07 18:35:28 +010057 in: `
58 a: <1
59 b: >= 0 & <= 10
60 c: != null
61 d: >100
62 `,
63 out: `<0>{a: <1, b: (>=0 & <=10), c: !=null, d: >100}`,
64 }, {
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +010065 in: "" +
66 `a: "\(4)",
67 b: "one \(a) two \( a + c )",
68 c: "one"`,
69 out: `<0>{a: ""+4+"", b: "one "+<0>.a+" two "+(<0>.a + <0>.c)+"", c: "one"}`,
70 }, {
71 in: "" +
72 `a: """
73 multi
74 """,
75 b: '''
76 hello world
77 goodbye globe
78 welcome back planet
79 '''`,
80 out: `<0>{a: "multi", b: 'hello world\ngoodbye globe\nwelcome back planet'}`,
81 }, {
82 in: "" +
83 `a: """
84 multi \(4)
85 """,
86 b: """
87 hello \("world")
88 goodbye \("globe")
89 welcome back \("planet")
90 """`,
91 out: `<0>{a: "multi "+4+"", b: "hello "+"world"+"\ngoodbye "+"globe"+"\nwelcome back "+"planet"+""}`,
92 }, {
93 in: `
94 a: _
95 b: int
96 c: float
97 d: bool
98 e: duration
99 f: string
100 `,
101 out: "<0>{a: _, b: int, c: float, d: bool, e: duration, f: string}",
102 }, {
103 in: `
104 a: null
105 b: true
106 c: false
107 `,
108 out: "<0>{a: null, b: true, c: false}",
109 }, {
110 in: `
111 null: null
112 true: true
113 false: false
114 `,
115 out: "<0>{null: null, true: true, false: false}",
116 }, {
117 in: `
118 a: 1 + 2
119 b: -2 - 3
120 c: !d
121 d: true
122 `,
123 out: "<0>{a: (1 + 2), b: (-2 - 3), c: !<0>.d, d: true}",
124 }, {
125 in: `
126 l0: 3*[int]
127 l0: [1, 2, 3]
Marcel van Lohuizen7d0797b2019-02-07 18:35:28 +0100128 l1: <=5*[string]
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100129 l1: ["a", "b"]
Marcel van Lohuizen7d0797b2019-02-07 18:35:28 +0100130 l2: (<=5)*[{ a: int }]
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100131 l2: [{a: 1}, {a: 2, b: 3}]
Marcel van Lohuizen7d0797b2019-02-07 18:35:28 +0100132 l3: (<=10)*[int]
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100133 l3: [1, 2, 3, ...]
134 l4: [1, 2, ...]
135 l4: [...int]
136 l5: [1, ...int]
137
Marcel van Lohuizen7d0797b2019-02-07 18:35:28 +0100138 s1: ((<=6)*[int])[2:3]
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100139 s2: [0,2,3][1:2]
140
Marcel van Lohuizen7d0797b2019-02-07 18:35:28 +0100141 e0: (>=2 & <=5)*[{}]
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100142 e0: [{}]
143 `,
Marcel van Lohuizen7d0797b2019-02-07 18:35:28 +0100144 out: `<0>{l0: ((3 * [int]) & [1,2,3]), l1: ((<=5 * [string]) & ["a","b"]), l2: ((<=5 * [<1>{a: int}]) & [<2>{a: 1},<3>{a: 2, b: 3}]), l3: ((<=10 * [int]) & [1,2,3, ...]), l4: ([1,2, ...] & [, ...int]), l5: [1, ...int], s1: (<=6 * [int])[2:3], s2: [0,2,3][1:2], e0: (((>=2 & <=5) * [<4>{}]) & [<5>{}])}`,
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100145 }, {
146 in: `
147 a: 5 | "a" | true
Marcel van Lohuizenc9b3cb22019-01-30 11:32:41 +0100148 aa: 5 | *"a" | true
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100149 b c: {
150 cc: { ccc: 3 }
151 }
152 d: true
153 `,
Marcel van Lohuizenc9b3cb22019-01-30 11:32:41 +0100154 out: "<0>{a: (5 | \"a\" | true), aa: (5 | *\"a\" | true), b: <1>{c: <2>{cc: <3>{ccc: 3}}}, d: true}",
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100155 }, {
156 in: `
157 a a: { b: a } // referencing ancestor nodes is legal.
158 a b: a.a // do lookup before merging of nodes
159 b: a.a // different node as a.a.b, as first node counts
160 c: a // same node as b, as first node counts
161 d: a["a"]
162 `,
163 out: `<0>{a: (<1>{a: <2>{b: <2>}} & <3>{b: <3>.a}), b: <0>.a.a, c: <0>.a, d: <0>.a["a"]}`,
164 }, {
165 // bunch of aliases
166 in: `
167 a1 = a2
168 a2 = 5
169 b: a1
170 a3 = d
171 c: {
172 d: {
173 r: a3
174 }
175 r: a3
176 }
177 d: { e: 4 }
178 `,
179 out: `<0>{b: 5, c: <1>{d: <2>{r: <0>.d}, r: <0>.d}, d: <3>{e: 4}}`,
180 }, {
181 // aliases with errors
182 in: `
183 e1 = 1
184 e1 = 2
185 e1v: e1
186 e2: "a"
187 e2 = "a"
188 `,
189 out: "cannot have two aliases with the same name in the same scope:\n" +
190 " test:3:3\n" +
191 "cannot have alias and non-alias with the same name:\n" +
192 " test:6:3\n" +
193 "<0>{}",
194 }, {
195 in: `
196 a = b
197 b: {
198 c: a // reference to own root.
199 }
200 `,
201 out: `<0>{b: <1>{c: <0>.b}}`,
202 }, {
203 in: `
204 a: {
205 <name>: { n: name }
206 k: 1
207 }
208 b: {
209 <x>: { x: 0, y: 1 }
210 v: {}
211 }
212 `,
213 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>{}}}`,
214 }, {
215 in: `
Marcel van Lohuizen76b92b52018-12-16 10:47:03 +0100216 a: { "\(k)": v for k, v in b if b.a < k }
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100217 b: {
218 a: 1
219 b: 2
220 c: 3
221 }
222 `,
Marcel van Lohuizen66db9202018-12-17 19:02:08 +0100223 out: `<0>{a: <1>{ <2>for k, v in <0>.b if (<0>.b.a < <2>.k) yield (""+<2>.k+""): <2>.v}, b: <3>{a: 1, b: 2, c: 3}}`,
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100224 }, {
225 in: `
Marcel van Lohuizen76b92b52018-12-16 10:47:03 +0100226 a: { "\(v)": v for k, v in b }
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100227 b: { a: "aa", b: "bb", c: "cc" }
228 `,
Marcel van Lohuizen66db9202018-12-17 19:02:08 +0100229 out: `<0>{a: <1>{ <2>for k, v in <0>.b yield (""+<2>.v+""): <2>.v}, b: <3>{a: "aa", b: "bb", c: "cc"}}`,
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100230 }, {
231 in: `
232 a: [ v for _, v in b ]
233 b: { a: 1, b: 2, c: 3 }
234 `,
235 out: `<0>{a: [ <1>for _, v in <0>.b yield (*nil*): <1>.v ], b: <2>{a: 1, b: 2, c: 3}}`,
236 }, {
237 in: `
Marcel van Lohuizen7d0797b2019-02-07 18:35:28 +0100238 a: >=1 & <=2
239 b: >=1 & >=2 & <=3
240 c: >="a" & <"b"
241 d: >(2+3) & <(4+5)
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100242 `,
Marcel van Lohuizen7d0797b2019-02-07 18:35:28 +0100243 out: `<0>{a: (>=1 & <=2), b: ((>=1 & >=2) & <=3), c: (>="a" & <"b"), d: (>(2 + 3) & <(4 + 5))}`,
Marcel van Lohuizenc9b3cb22019-01-30 11:32:41 +0100244 }, {
245 in: `
246 a: *1,
247 b: **1 | 2
248 `,
249 out: `<0>{a: _|_(preference mark not allowed at this position), ` +
250 `b: (*_|_(preference mark not allowed at this position) | 2)}`,
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100251 }}
252 for _, tc := range testCases {
253 t.Run("", func(t *testing.T) {
254 ctx, root, errs := compileFileWithErrors(t, tc.in)
255 buf := &bytes.Buffer{}
256 if len(errs) > 0 {
257 errors.Print(buf, errs)
258 }
259 buf.WriteString(debugStr(ctx, root))
260 got := buf.String()
261 if got != tc.out {
262 t.Errorf("output differs:\ngot %q\nwant %q", got, tc.out)
263 }
264 })
265 }
266}
267
268func TestEmit(t *testing.T) {
269 testCases := []struct {
270 in string
271 out string
272 rw rewriteMode
273 }{{
274 in: `"\(hello), \(world)!"` + `
275 hello: "Hello"
276 world: "World"
277 `,
278 out: `""+<0>.hello+", "+<0>.world+"!"`,
279 rw: evalRaw,
280 }, {
281 in: `"\(hello), \(world)!"` + `
282 hello: "Hello"
283 world: "World"
284 `,
285 out: `"Hello, World!"`,
286 rw: evalPartial,
287 }, {
288 // Ambiguous disjunction must cary over to emit value.
289 in: `baz
290
291 baz: {
292 a: 8000 | 7080
293 a: 7080 | int
294 }`,
Marcel van Lohuizenc9b3cb22019-01-30 11:32:41 +0100295 out: `<0>{a: _|_((8000 | 7080):more than one element remaining (8000 and 7080))}`,
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100296 rw: evalFull,
297 }}
298 for _, tc := range testCases {
299 t.Run("", func(t *testing.T) {
300 ctx, root := compileFile(t, tc.in)
301 v := testResolve(ctx, root.emit, tc.rw)
302 if got := debugStr(ctx, v); got != tc.out {
303 t.Errorf("output differs:\ngot %q\nwant %q", got, tc.out)
304 }
305 })
306 }
307}
Marcel van Lohuizen7b5d2fd2018-12-11 16:34:56 +0100308
309func TestEval(t *testing.T) {
310 testCases := []struct {
311 in string
312 expr string
313 out string
314 }{{
315 in: `
316 hello: "Hello"
317 world: "World"
318 `,
319 expr: `"\(hello), \(world)!"`,
320 out: `"Hello, World!"`,
321 }, {
322 in: `
323 a: { b: 2, c: 3 }
324 z: 1
325 `,
326 expr: `a.b + a.c + z`,
327 out: `6`,
328 }, {
329 in: `
330 a: { b: 2, c: 3 }
331 `,
332 expr: `{ d: a.b + a.c }`,
333 out: `<0>{d: 5}`,
334 }, {
335 in: `
336 a: "Hello World!"
337 `,
338 expr: `strings.ToUpper(a)`,
339 out: `"HELLO WORLD!"`,
340 }, {
341 in: `
342 a: 0x8
343 b: 0x1`,
344 expr: `bits.Or(a, b)`, // package shorthand
345 out: `9`,
346 }, {
347 in: `
348 a: 0x8
349 b: 0x1`,
350 expr: `math.Or(a, b)`,
351 out: `_|_(<0>.Or:undefined field "Or")`,
352 }, {
353 in: `a: 0x8`,
354 expr: `mathematics.Abs(a)`,
355 out: `_|_(reference "mathematics" not found)`,
356 }}
357 for _, tc := range testCases {
358 t.Run("", func(t *testing.T) {
359 ctx, inst, errs := compileInstance(t, tc.in)
360 if errs != nil {
361 t.Fatal(errs)
362 }
363 expr, err := parser.ParseExpr(token.NewFileSet(), "<test>", tc.expr)
364 if err != nil {
365 t.Fatal(err)
366 }
367 evaluated := inst.evalExpr(ctx, expr)
368 v := testResolve(ctx, evaluated, evalFull)
369 if got := debugStr(ctx, v); got != tc.out {
370 t.Errorf("output differs:\ngot %q\nwant %q", got, tc.out)
371 }
372 })
373 }
374}