| // Copyright 2019 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 diff |
| |
| import ( |
| "bytes" |
| "testing" |
| |
| "cuelang.org/go/cue" |
| ) |
| |
| func TestDiff(t *testing.T) { |
| testCases := []struct { |
| name string |
| x, y string |
| kind Kind |
| diff string |
| profile *Profile |
| }{{ |
| name: "identity struct", |
| x: `{ |
| a: { |
| b: 1 |
| c: 2 |
| } |
| l: { |
| d: 1 |
| } |
| }`, |
| y: `{ |
| a: { |
| c: 2 |
| b: 1 |
| } |
| l: { |
| d: 1 |
| } |
| }`, |
| }, { |
| name: "identity list", |
| x: `[1, 2, 3]`, |
| y: `[1, 2, 3]`, |
| }, { |
| name: "identity value", |
| x: `"foo"`, |
| y: `"foo"`, |
| }, { |
| name: "modified value", |
| x: `"foo"`, |
| y: `"bar"`, |
| kind: Modified, |
| diff: `- "foo", |
| + "bar", |
| `, |
| }, { |
| name: "basics", |
| x: `{ |
| a: int |
| b: 2 |
| s: 4 |
| d: 1 |
| e: null |
| } |
| `, |
| y: ` |
| { |
| a: string |
| c: 3 |
| s: 4 |
| d: int |
| e: null |
| } |
| `, |
| kind: Modified, |
| diff: ` { |
| - a: int |
| + a: string |
| - b: 2 |
| + c: 3 |
| s: 4 |
| - d: 1 |
| + d: int |
| e: null |
| } |
| `, |
| }, { |
| name: "basics 2", |
| x: `{ |
| ls: [2, 3, 4] |
| "foo-bar": 2 |
| s: 4 |
| lm1: [2, 3, 5] |
| lm2: [6] |
| } |
| `, |
| y: ` |
| { |
| ls: [2, 3, 4] |
| "foo-bar": 3 |
| s: 4 |
| lm1: [2, 3, 4, 6] |
| lm2: [] |
| la: [2, 3, 4] |
| } |
| `, |
| kind: Modified, |
| diff: ` { |
| ls: [2, 3, 4] |
| - "foo-bar": 2 |
| + "foo-bar": 3 |
| s: 4 |
| lm1: [ |
| 2, |
| 3, |
| - 5, |
| + 4, |
| + 6, |
| ] |
| lm2: [ |
| - 6, |
| ] |
| + la: [2, 3, 4] |
| } |
| `, |
| }, { |
| name: "interupted run 1", |
| x: `{ |
| a: 1 |
| b: 2 |
| c: 3 |
| d: 4 |
| e: 10 |
| f: 6 |
| g: 7 |
| h: 8 |
| i: 9 |
| j: 10 |
| } |
| `, |
| y: ` |
| { |
| a: 1 |
| b: 2 |
| c: 3 |
| d: 4 |
| e: 5 |
| f: 6 |
| g: 7 |
| h: 8 |
| i: 9 |
| j: 10 |
| } |
| `, |
| kind: Modified, |
| diff: ` { |
| ... // 2 identical elements |
| c: 3 |
| d: 4 |
| - e: 10 |
| + e: 5 |
| f: 6 |
| g: 7 |
| ... // 3 identical elements |
| } |
| `, |
| }, { |
| name: "interupted run 2", |
| x: `{ |
| a: -1 |
| b: 2 |
| c: 3 |
| d: 4 |
| e: 5 |
| f: 6 |
| g: 7 |
| h: 8 |
| i: 9 |
| j: -10 |
| }`, |
| y: `{ |
| a: 1 |
| b: 2 |
| c: 3 |
| d: 4 |
| e: 5 |
| f: 6 |
| g: 7 |
| h: 8 |
| i: 9 |
| j: 10 |
| } |
| `, |
| kind: Modified, |
| diff: ` { |
| - a: -1 |
| + a: 1 |
| b: 2 |
| c: 3 |
| ... // 4 identical elements |
| h: 8 |
| i: 9 |
| - j: -10 |
| + j: 10 |
| } |
| `, |
| }, { |
| name: "recursion", |
| x: `{ |
| s: { |
| a: 1 |
| b: 3 |
| d: 4 |
| } |
| l: [ |
| [3, 4] |
| ] |
| }`, |
| y: `{ |
| s: { |
| a: 2 |
| b: 3 |
| c: 4 |
| } |
| l: [ |
| [3, 5, 6] |
| ] |
| } |
| `, |
| kind: Modified, |
| diff: ` { |
| s: { |
| - a: 1 |
| + a: 2 |
| b: 3 |
| - d: 4 |
| + c: 4 |
| } |
| l: [ |
| [ |
| 3, |
| - 4, |
| + 5, |
| + 6, |
| ] |
| ] |
| } |
| `, |
| }, { |
| name: "optional and definitions", |
| x: `{ |
| #s: { |
| #a: 1 |
| b: 2 |
| } |
| o?: 3 |
| #od?: 1 |
| oc?: 5 |
| }`, |
| y: `{ |
| #s: { |
| a: 2 |
| #b: 2 |
| } |
| o?: 4 |
| #od: 1 |
| #oc?: 5 |
| } |
| `, |
| kind: Modified, |
| diff: ` { |
| #s: { |
| - #a: 1 |
| - b: 2 |
| + a: 2 |
| + #b: 2 |
| } |
| - o?: 3 |
| + o?: 4 |
| - #od?: 1 |
| + #od: 1 |
| - oc?: 5 |
| + #oc?: 5 |
| } |
| `, |
| }, { |
| name: "bulk optional", |
| x: `{[_]: x: "hello"} |
| |
| a: x: "hello" |
| `, |
| y: `[_]: x: "hello" |
| |
| `, |
| kind: Modified, |
| diff: ` { |
| - a: { |
| - x: "hello" |
| - } |
| } |
| `, |
| }, { |
| x: ` |
| #Directory: { |
| { |
| // Directory from another directory (e.g. subdirectory) |
| from: #Directory |
| } | { |
| // Reference to remote directory |
| ref: string |
| } | { |
| // Use a local directory |
| local: string |
| } |
| path: string | *"/" |
| } |
| `, |
| y: ` |
| #Directory: { |
| { |
| // Directory from another directory (e.g. subdirectory) |
| from: #Directory |
| } | { |
| // Reference to remote directory |
| ref: string |
| } | { |
| // Use a local directory |
| local: string |
| } |
| path: string | *"/" |
| } |
| `, |
| profile: Final, |
| }, { |
| x: ` |
| #Directory: { |
| { |
| // Directory from another directory (e.g. subdirectory) |
| from: #Directory |
| } | { |
| // Reference to remote directory |
| ref: string |
| } | { |
| // Use a local directory |
| local: string |
| } |
| path: string | *"/" |
| } |
| `, |
| y: ` |
| #Directory: { |
| { |
| // Directory from another directory (e.g. subdirectory) |
| from: #Directory |
| } | { |
| // Reference to remote directory |
| ref: string |
| } | { |
| // Use a local directory |
| local: string |
| } |
| path: string | *"/" |
| } |
| `, |
| }} |
| for _, tc := range testCases { |
| t.Run(tc.name, func(t *testing.T) { |
| var r cue.Runtime |
| x, err := r.Compile("x", tc.x) |
| if err != nil { |
| t.Fatal(err) |
| } |
| y, err := r.Compile("y", tc.y) |
| if err != nil { |
| t.Fatal(err) |
| } |
| p := tc.profile |
| if p == nil { |
| p = Schema |
| } |
| kind, script := p.Diff(x.Value(), y.Value()) |
| if kind != tc.kind { |
| t.Fatalf("got %d; want %d", kind, tc.kind) |
| } |
| if script != nil { |
| w := &bytes.Buffer{} |
| err = Print(w, script) |
| if err != nil { |
| t.Fatal(err) |
| } |
| if got := w.String(); got != tc.diff { |
| t.Errorf("\ngot\n%s;\nwant\n%s", got, tc.diff) |
| } |
| } |
| }) |
| } |
| } |
| |
| func TestX(t *testing.T) { |
| t.Skip() |
| |
| tc := struct { |
| x, y string |
| kind Kind |
| diff string |
| }{ |
| x: `{ |
| } |
| `, |
| y: ` |
| { |
| } |
| `, |
| kind: Modified, |
| diff: ``, |
| } |
| var r cue.Runtime |
| x, err := r.Compile("x", tc.x) |
| if err != nil { |
| t.Fatal(err) |
| } |
| y, err := r.Compile("y", tc.y) |
| if err != nil { |
| t.Fatal(err) |
| } |
| kind, script := Diff(x.Value(), y.Value()) |
| if kind != tc.kind { |
| t.Fatalf("got %d; want %d", kind, tc.kind) |
| } |
| w := &bytes.Buffer{} |
| err = Print(w, script) |
| if err != nil { |
| t.Fatal(err) |
| } |
| if got := w.String(); got != tc.diff { |
| t.Errorf("got\n%s;\nwant\n%s", got, tc.diff) |
| } |
| } |