cuelang.org/go@v0.10.1/internal/diff/diff_test.go (about) 1 // Copyright 2019 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 15 package diff 16 17 import ( 18 "bytes" 19 "testing" 20 21 "cuelang.org/go/cue" 22 "cuelang.org/go/cue/cuecontext" 23 "cuelang.org/go/internal/cuetdtest" 24 ) 25 26 func TestDiff(t *testing.T) { 27 testCases := []struct { 28 name string 29 x, y string 30 kind Kind 31 diff string 32 profile *Profile 33 }{{ 34 name: "identity struct", 35 x: `{ 36 a: { 37 b: 1 38 c: 2 39 } 40 l: { 41 d: 1 42 } 43 }`, 44 y: `{ 45 a: { 46 c: 2 47 b: 1 48 } 49 l: { 50 d: 1 51 } 52 }`, 53 }, { 54 name: "identity list", 55 x: `[1, 2, 3]`, 56 y: `[1, 2, 3]`, 57 }, { 58 name: "identity value", 59 x: `"foo"`, 60 y: `"foo"`, 61 }, { 62 name: "modified value", 63 x: `"foo"`, 64 y: `"bar"`, 65 kind: Modified, 66 diff: `- "foo", 67 + "bar", 68 `, 69 }, { 70 name: "basics", 71 x: `{ 72 a: int 73 b: 2 74 s: 4 75 d: 1 76 e: null 77 } 78 `, 79 y: ` 80 { 81 a: string 82 c: 3 83 s: 4 84 d: int 85 e: null 86 } 87 `, 88 kind: Modified, 89 diff: ` { 90 - a: int 91 + a: string 92 - b: 2 93 + c: 3 94 s: 4 95 - d: 1 96 + d: int 97 e: null 98 } 99 `, 100 }, { 101 name: "basics 2", 102 x: `{ 103 ls: [2, 3, 4] 104 "foo-bar": 2 105 s: 4 106 lm1: [2, 3, 5] 107 lm2: [6] 108 } 109 `, 110 y: ` 111 { 112 ls: [2, 3, 4] 113 "foo-bar": 3 114 s: 4 115 lm1: [2, 3, 4, 6] 116 lm2: [] 117 la: [2, 3, 4] 118 } 119 `, 120 kind: Modified, 121 diff: ` { 122 ls: [2, 3, 4] 123 - "foo-bar": 2 124 + "foo-bar": 3 125 s: 4 126 lm1: [ 127 2, 128 3, 129 - 5, 130 + 4, 131 + 6, 132 ] 133 lm2: [ 134 - 6, 135 ] 136 + la: [2, 3, 4] 137 } 138 `, 139 }, { 140 name: "interupted run 1", 141 x: `{ 142 a: 1 143 b: 2 144 c: 3 145 d: 4 146 e: 10 147 f: 6 148 g: 7 149 h: 8 150 i: 9 151 j: 10 152 } 153 `, 154 y: ` 155 { 156 a: 1 157 b: 2 158 c: 3 159 d: 4 160 e: 5 161 f: 6 162 g: 7 163 h: 8 164 i: 9 165 j: 10 166 } 167 `, 168 kind: Modified, 169 diff: ` { 170 ... // 2 identical elements 171 c: 3 172 d: 4 173 - e: 10 174 + e: 5 175 f: 6 176 g: 7 177 ... // 3 identical elements 178 } 179 `, 180 }, { 181 name: "interupted run 2", 182 x: `{ 183 a: -1 184 b: 2 185 c: 3 186 d: 4 187 e: 5 188 f: 6 189 g: 7 190 h: 8 191 i: 9 192 j: -10 193 }`, 194 y: `{ 195 a: 1 196 b: 2 197 c: 3 198 d: 4 199 e: 5 200 f: 6 201 g: 7 202 h: 8 203 i: 9 204 j: 10 205 } 206 `, 207 kind: Modified, 208 diff: ` { 209 - a: -1 210 + a: 1 211 b: 2 212 c: 3 213 ... // 4 identical elements 214 h: 8 215 i: 9 216 - j: -10 217 + j: 10 218 } 219 `, 220 }, { 221 name: "recursion", 222 x: `{ 223 s: { 224 a: 1 225 b: 3 226 d: 4 227 } 228 l: [ 229 [3, 4] 230 ] 231 }`, 232 y: `{ 233 s: { 234 a: 2 235 b: 3 236 c: 4 237 } 238 l: [ 239 [3, 5, 6] 240 ] 241 } 242 `, 243 kind: Modified, 244 diff: ` { 245 s: { 246 - a: 1 247 + a: 2 248 b: 3 249 - d: 4 250 + c: 4 251 } 252 l: [ 253 [ 254 3, 255 - 4, 256 + 5, 257 + 6, 258 ] 259 ] 260 } 261 `, 262 }, { 263 name: "optional and definitions", 264 x: `{ 265 #s: { 266 #a: 1 267 b: 2 268 } 269 o?: 3 270 #od?: 1 271 oc?: 5 272 }`, 273 y: `{ 274 #s: { 275 a: 2 276 #b: 2 277 } 278 o?: 4 279 #od: 1 280 #oc?: 5 281 } 282 `, 283 kind: Modified, 284 diff: ` { 285 #s: { 286 - #a: 1 287 - b: 2 288 + a: 2 289 + #b: 2 290 } 291 - o?: 3 292 + o?: 4 293 - #od?: 1 294 + #od: 1 295 - oc?: 5 296 + #oc?: 5 297 } 298 `, 299 }, { 300 name: "bulk optional", 301 x: `{[_]: x: "hello"} 302 303 a: x: "hello" 304 `, 305 y: `[_]: x: "hello" 306 307 `, 308 kind: Modified, 309 diff: ` { 310 - a: { 311 - x: "hello" 312 - } 313 } 314 `, 315 }, { 316 x: ` 317 #Directory: { 318 { 319 // Directory from another directory (e.g. subdirectory) 320 from: #Directory 321 } | { 322 // Reference to remote directory 323 ref: string 324 } | { 325 // Use a local directory 326 local: string 327 } 328 path: string | *"/" 329 } 330 `, 331 y: ` 332 #Directory: { 333 { 334 // Directory from another directory (e.g. subdirectory) 335 from: #Directory 336 } | { 337 // Reference to remote directory 338 ref: string 339 } | { 340 // Use a local directory 341 local: string 342 } 343 path: string | *"/" 344 } 345 `, 346 profile: Final, 347 }, { 348 x: ` 349 #Directory: { 350 { 351 // Directory from another directory (e.g. subdirectory) 352 from: #Directory 353 } | { 354 // Reference to remote directory 355 ref: string 356 } | { 357 // Use a local directory 358 local: string 359 } 360 path: string | *"/" 361 } 362 `, 363 y: ` 364 #Directory: { 365 { 366 // Directory from another directory (e.g. subdirectory) 367 from: #Directory 368 } | { 369 // Reference to remote directory 370 ref: string 371 } | { 372 // Use a local directory 373 local: string 374 } 375 path: string | *"/" 376 } 377 `, 378 }, { 379 name: "hidden fields", 380 x: `{a: 1, _hidden1: 1, _hidden: 1}`, 381 y: `{a: 1, _hidden2: 1, _hidden: 2}`, 382 diff: ` { 383 a: 1 384 - _hidden1: 1 385 + _hidden2: 1 386 - _hidden: 1 387 + _hidden: 2 388 } 389 `, 390 kind: Modified, 391 }, { 392 name: "ignore hidden fields in schema", 393 x: `{a: 1, _hidden1: 1, _hidden: 1}`, 394 y: `{a: 1, _hidden2: 1, _hidden: 2}`, 395 profile: &Profile{SkipHidden: true}, 396 }, { 397 name: "ignore hidden fields in data", 398 x: `{a: 1, _hidden1: 1, _hidden: 1}`, 399 y: `{a: 1, _hidden2: 1, _hidden: 2}`, 400 profile: &Profile{SkipHidden: true, Concrete: true}, 401 }, { 402 name: "all errors are equal", 403 x: `1 & 3`, 404 y: `1 & 4`, 405 }} 406 for _, tc := range testCases { 407 cuetdtest.FullMatrix.Run(t, tc.name, func(t *cuetdtest.M) { 408 ctx := t.Context() 409 // it is not fatal if x or y contain errors: some test cases 410 // rely on interacting with such errors. 411 x := ctx.CompileString(tc.x, cue.Filename("x")) 412 y := ctx.CompileString(tc.y, cue.Filename("y")) 413 p := tc.profile 414 if p == nil { 415 p = Schema 416 } 417 kind, script := p.Diff(x, y) 418 if kind != tc.kind { 419 t.Fatalf("got %d; want %d", kind, tc.kind) 420 } 421 if script != nil { 422 w := &bytes.Buffer{} 423 err := Print(w, script) 424 if err != nil { 425 t.Fatal(err) 426 } 427 if got := w.String(); got != tc.diff { 428 t.Errorf("\ngot\n%s;\nwant\n%s", got, tc.diff) 429 } 430 } 431 }) 432 } 433 } 434 435 func TestX(t *testing.T) { 436 t.Skip() 437 438 tc := struct { 439 x, y string 440 kind Kind 441 diff string 442 }{ 443 x: `{ 444 } 445 `, 446 y: ` 447 { 448 } 449 `, 450 kind: Modified, 451 diff: ``, 452 } 453 ctx := cuecontext.New() 454 // it is not fatal if x or y contain errors: some test cases 455 // rely on interacting with such errors. 456 x := ctx.CompileString(tc.x, cue.Filename("x")) 457 y := ctx.CompileString(tc.y, cue.Filename("y")) 458 459 kind, script := Diff(x, y) 460 if kind != tc.kind { 461 t.Fatalf("got %d; want %d", kind, tc.kind) 462 } 463 w := &bytes.Buffer{} 464 err := Print(w, script) 465 if err != nil { 466 t.Fatal(err) 467 } 468 if got := w.String(); got != tc.diff { 469 t.Errorf("got\n%s;\nwant\n%s", got, tc.diff) 470 } 471 }