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  }