blob: c4289e67f70cecb9eda1e3e6c456daa487ccfeae [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 "fmt"
20 "log"
21 "strings"
22 "testing"
23
24 "cuelang.org/go/cue/format"
25)
26
27func TestExport(t *testing.T) {
28 testCases := []struct {
Marcel van Lohuizen466e3f62019-04-06 14:16:50 +020029 raw bool // skip evaluation the root, fully raw
30 eval bool // evaluate the full export
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +010031 in, out string
32 }{{
33 in: `"hello"`,
34 out: `"hello"`,
35 }, {
36 in: `'hello'`,
37 out: `'hello'`,
38 }, {
39 in: `'hello\nworld'`,
40 out: "'''" +
41 multiSep + "hello" +
42 multiSep + "world" +
43 multiSep + "'''",
44 }, {
45 in: `"hello\nworld"`,
46 out: `"""` +
47 multiSep + "hello" +
48 multiSep + "world" +
49 multiSep + `"""`,
50 }, {
51 in: "{ a: 1, b: a + 2, c: null, d: true, e: _, f: string }",
52 out: unindent(`
53 {
54 a: 1
55 b: 3
56 c: null
57 d: true
58 e: _
59 f: string
60 }`),
61 }, {
62 in: `{ a: { b: 2.0, s: "abc" }, b: a.b, c: a.c, d: a["d"], e: a.t[2:3] }`,
63 out: unindent(`
64 {
65 a: {
66 b: 2.0
67 s: "abc"
68 }
69 b: 2.0
70 c: _|_ // undefined field "c"
71 d: _|_ // undefined field "d"
72 e: _|_ // undefined field "t"
73 }`),
74 }, {
75 in: `{
76 a: 5*[int]
77 a: [1, 2, ...]
Marcel van Lohuizen7d0797b2019-02-07 18:35:28 +010078 b: <=5*[int]
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +010079 b: [1, 2, ...]
Marcel van Lohuizen7d0797b2019-02-07 18:35:28 +010080 c: (>=3 & <=5)*[int]
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +010081 c: [1, 2, ...]
Marcel van Lohuizen7d0797b2019-02-07 18:35:28 +010082 d: >=2*[int]
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +010083 d: [1, 2, ...]
84 e: [...int]
85 e: [1, 2, ...]
86 f: [1, 2, ...]
87 }`,
88 out: unindent(`
Marcel van Lohuizen0018c742019-02-20 01:25:39 +010089 {
Jonathan Amsterdam0500c312019-02-16 18:04:09 -050090 a: [1, 2, int, int, int]
91 b: <=5*[int] & [1, 2, ...]
92 c: (>=3 & <=5)*[int] & [1, 2, ...]
93 d: >=2*[int] & [1, 2, ...]
Marcel van Lohuizenb134a502019-05-06 11:33:05 +020094 e: [1, 2]
95 f: [1, 2]
96 }`),
97 }, {
98 raw: true,
99 in: `{
100 a: 5*[int]
101 a: [1, 2, ...]
102 b: <=5*[int]
103 b: [1, 2, ...]
104 c: (>=3 & <=5)*[int]
105 c: [1, 2, ...]
106 d: >=2*[int]
107 d: [1, 2, ...]
108 e: [...int]
109 e: [1, 2, ...]
110 f: [1, 2, ...]
111 }`,
112 out: unindent(`
113 {
114 a: 5*[int] & [1, 2, ...]
115 b: <=5*[int] & [1, 2, ...]
116 c: (>=3 & <=5)*[int] & [1, 2, ...]
117 d: >=2*[int] & [1, 2, ...]
118 e: [...int] & [1, 2, ...]
Marcel van Lohuizen0018c742019-02-20 01:25:39 +0100119 f: [1, 2, ...]
120 }`),
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100121 }, {
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100122 raw: true,
123 in: `{ a: { b: [] }, c: a.b, d: a["b"] }`,
124 out: unindent(`
125 {
126 a b: []
127 c: a.b
128 d: a["b"]
129 }`),
130 }, {
131 raw: true,
Marcel van Lohuizenc9b3cb22019-01-30 11:32:41 +0100132 in: `{ a: *"foo" | *"bar" | *string | int, b: a[2:3] }`,
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100133 out: unindent(`
134 {
Marcel van Lohuizenc9b3cb22019-01-30 11:32:41 +0100135 a: *"foo" | *"bar" | *string | int
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100136 b: a[2:3]
137 }`),
138 }, {
Marcel van Lohuizen7d0797b2019-02-07 18:35:28 +0100139 in: `{
140 a: >=0 & <=10 & !=1
141 }`,
142 out: unindent(`
143 {
144 a: >=0 & <=10 & !=1
145 }`),
146 }, {
147 raw: true,
148 in: `{
149 a: >=0 & <=10 & !=1
150 }`,
151 out: unindent(`
152 {
153 a: >=0 & <=10 & !=1
154 }`),
155 }, {
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100156 raw: true,
Marcel van Lohuizen3d30a782019-02-18 23:32:10 +0100157 in: `{ a: [1, 2], b: { "\(k)": v for k, v in a if v > 1 } }`,
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100158 out: unindent(`
159 {
160 a: [1, 2]
161 b: {
Marcel van Lohuizen3d30a782019-02-18 23:32:10 +0100162 "\(k)": v for k, v in a if v > 1
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100163 }
164 }`),
165 }, {
166 raw: true,
167 in: `{ a: [1, 2], b: [ v for k, v in a ] }`,
168 out: unindent(`
169 {
170 a: [1, 2]
171 b: [ v for k, v in a ]
172 }`),
173 }, {
174 raw: true,
Marcel van Lohuizen7d0797b2019-02-07 18:35:28 +0100175 in: `{ a: >=0 & <=10, b: "Count: \(a) times" }`,
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100176 out: unindent(`
177 {
Marcel van Lohuizen7d0797b2019-02-07 18:35:28 +0100178 a: >=0 & <=10
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100179 b: "Count: \(a) times"
180 }`),
Marcel van Lohuizen7b5d2fd2018-12-11 16:34:56 +0100181 }, {
182 raw: true,
183 in: `{ a: "", b: len(a) }`,
184 out: unindent(`
185 {
186 a: ""
187 b: len(a)
188 }`),
Marcel van Lohuizen0018c742019-02-20 01:25:39 +0100189 }, {
190 raw: true,
Marcel van Lohuizen466e3f62019-04-06 14:16:50 +0200191 eval: true,
Marcel van Lohuizen0018c742019-02-20 01:25:39 +0100192 in: `{
193 b: {
194 idx: a[str]
195 str: string
196 }
197 b a b: 4
198 a b: 3
199 }`,
200 // reference to a must be redirected to outer a through alias
201 out: unindent(`
202 {
203 A = a
204 b: {
205 idx: A[str]
206 a b: 4
207 str: string
208 }
209 a b: 3
210 }`),
211 }, {
212 raw: true,
Marcel van Lohuizen466e3f62019-04-06 14:16:50 +0200213 eval: true,
Marcel van Lohuizen0018c742019-02-20 01:25:39 +0100214 in: `{
Marcel van Lohuizen5f5d39f2019-02-21 23:30:24 +0100215 job <Name>: {
216 name: Name
Marcel van Lohuizenb9b62d32019-03-14 23:50:15 +0100217 replicas: uint | *1 @protobuf(10)
Marcel van Lohuizen5f5d39f2019-02-21 23:30:24 +0100218 command: string
219 }
220
221 job list command: "ls"
222
223 job nginx: {
224 command: "nginx"
225 replicas: 2
226 }
Marcel van Lohuizen0018c742019-02-20 01:25:39 +0100227 }`,
Marcel van Lohuizen0018c742019-02-20 01:25:39 +0100228 out: unindent(`
229 {
Marcel van Lohuizen5f5d39f2019-02-21 23:30:24 +0100230 job: {
231 list: {
232 name: "list"
Marcel van Lohuizenb9b62d32019-03-14 23:50:15 +0100233 replicas: 1 @protobuf(10)
Marcel van Lohuizen5f5d39f2019-02-21 23:30:24 +0100234 command: "ls"
235 }
236 nginx: {
237 name: "nginx"
Marcel van Lohuizenb9b62d32019-03-14 23:50:15 +0100238 replicas: 2 @protobuf(10)
Marcel van Lohuizen5f5d39f2019-02-21 23:30:24 +0100239 command: "nginx"
240 }
241 }
Marcel van Lohuizen0018c742019-02-20 01:25:39 +0100242 }`),
Marcel van Lohuizen5f5d39f2019-02-21 23:30:24 +0100243 }, {
244 raw: true,
Marcel van Lohuizen466e3f62019-04-06 14:16:50 +0200245 eval: true,
Marcel van Lohuizen5f5d39f2019-02-21 23:30:24 +0100246 in: `{
247 b: [{
248 <X>: int
249 f: 4 if a > 4
250 }][a]
251 a: int
252 c: *1 | 2
253 }`,
254 // reference to a must be redirected to outer a through alias
255 out: unindent(`
256 {
257 b: [{
258 <X>: int
259 "f": 4 if a > 4
260 }][a]
261 a: int
262 c: 1
263 }`)}}
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100264 for _, tc := range testCases {
265 t.Run("", func(t *testing.T) {
266 body := fmt.Sprintf("Test: %s", tc.in)
267 ctx, obj := compileFile(t, body)
268 ctx.trace = *traceOn
269 var root value = obj
270 if !tc.raw {
271 root = testResolve(ctx, obj, evalFull)
272 }
273 t.Log(debugStr(ctx, root))
274
275 n := root.(*structLit).arcs[0].v
276 v := newValueRoot(ctx, n)
277
278 buf := &bytes.Buffer{}
Marcel van Lohuizen466e3f62019-04-06 14:16:50 +0200279 opts := options{raw: !tc.eval}
280 err := format.Node(buf, export(ctx, v.eval(ctx), opts))
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100281 if err != nil {
282 log.Fatal(err)
283 }
284 if got := buf.String(); got != tc.out {
285 t.Errorf("\ngot %v;\nwant %v", got, tc.out)
286 }
287 })
288 }
289}
290
291func unindent(s string) string {
292 lines := strings.Split(s, "\n")[1:]
293 ws := lines[0][:len(lines[0])-len(strings.TrimLeft(lines[0], " \t"))]
294 for i, s := range lines {
295 if s == "" {
296 continue
297 }
298 if !strings.HasPrefix(s, ws) {
299 panic("invalid indentation")
300 }
301 lines[i] = lines[i][len(ws):]
302 }
303 return strings.Join(lines, "\n")
304}