cuelang.org/go@v0.10.1/internal/core/subsume/value_test.go (about)

     1  // Copyright 2020 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 subsume_test
    16  
    17  import (
    18  	"regexp"
    19  	"strconv"
    20  	"strings"
    21  	"testing"
    22  
    23  	"cuelang.org/go/cue/parser"
    24  	"cuelang.org/go/internal/core/adt"
    25  	"cuelang.org/go/internal/core/compile"
    26  	"cuelang.org/go/internal/core/eval"
    27  	"cuelang.org/go/internal/core/subsume"
    28  	"cuelang.org/go/internal/cuetdtest"
    29  )
    30  
    31  const (
    32  	subNone = iota
    33  	subFinal
    34  	subNoOptional
    35  	subSchema
    36  	subDefaults
    37  )
    38  
    39  func TestValues(t *testing.T) {
    40  	// Do not inline: the named struct is used as a marker in
    41  	// testdata/gen.go.
    42  	type subsumeTest struct {
    43  		// the result of b ⊑ a, where a and b are defined in "in"
    44  		subsumes bool
    45  		in       string
    46  		mode     int
    47  
    48  		skip_v2 bool // Bug only exists in v2. Won't fix.
    49  	}
    50  	testCases := []subsumeTest{
    51  		// Top subsumes everything
    52  		0: {subsumes: true, in: `a: _, b: _ `},
    53  		1: {subsumes: true, in: `a: _, b: null `},
    54  		2: {subsumes: true, in: `a: _, b: int `},
    55  		3: {subsumes: true, in: `a: _, b: 1 `},
    56  		4: {subsumes: true, in: `a: _, b: float `},
    57  		5: {subsumes: true, in: `a: _, b: "s" `},
    58  		6: {subsumes: true, in: `a: _, b: {} `},
    59  		7: {subsumes: true, in: `a: _, b: []`},
    60  		8: {subsumes: true, in: `a: _, b: _|_ `},
    61  
    62  		// Nothing besides top subsumed top
    63  		9:  {subsumes: false, in: `a: null,    b: _`},
    64  		10: {subsumes: false, in: `a: int, b: _`},
    65  		11: {subsumes: false, in: `a: 1,       b: _`},
    66  		12: {subsumes: false, in: `a: float, b: _`},
    67  		13: {subsumes: false, in: `a: "s",     b: _`},
    68  		14: {subsumes: false, in: `a: {},      b: _`},
    69  		15: {subsumes: false, in: `a: [],      b: _`},
    70  		16: {subsumes: false, in: `a: _|_ ,      b: _`},
    71  
    72  		// Bottom subsumes nothing except bottom itself.
    73  		17: {subsumes: false, in: `a: _|_, b: null `},
    74  		18: {subsumes: false, in: `a: _|_, b: int `},
    75  		19: {subsumes: false, in: `a: _|_, b: 1 `},
    76  		20: {subsumes: false, in: `a: _|_, b: float `},
    77  		21: {subsumes: false, in: `a: _|_, b: "s" `},
    78  		22: {subsumes: false, in: `a: _|_, b: {} `},
    79  		23: {subsumes: false, in: `a: _|_, b: [] `},
    80  		24: {subsumes: true, in: ` a: _|_, b: _|_ `},
    81  
    82  		// All values subsume bottom
    83  		25: {subsumes: true, in: `a: null,    b: _|_`},
    84  		26: {subsumes: true, in: `a: int, b: _|_`},
    85  		27: {subsumes: true, in: `a: 1,       b: _|_`},
    86  		28: {subsumes: true, in: `a: float, b: _|_`},
    87  		29: {subsumes: true, in: `a: "s",     b: _|_`},
    88  		30: {subsumes: true, in: `a: {},      b: _|_`},
    89  		31: {subsumes: true, in: `a: [],      b: _|_`},
    90  		32: {subsumes: true, in: `a: true,    b: _|_`},
    91  		33: {subsumes: true, in: `a: _|_,       b: _|_`},
    92  
    93  		// null subsumes only null
    94  		34: {subsumes: true, in: ` a: null, b: null `},
    95  		35: {subsumes: false, in: `a: null, b: 1 `},
    96  		36: {subsumes: false, in: `a: 1,    b: null `},
    97  
    98  		37: {subsumes: true, in: ` a: true, b: true `},
    99  		38: {subsumes: false, in: `a: true, b: false `},
   100  
   101  		39: {subsumes: true, in: ` a: "a",    b: "a" `},
   102  		40: {subsumes: false, in: `a: "a",    b: "b" `},
   103  		41: {subsumes: true, in: ` a: string, b: "a" `},
   104  		42: {subsumes: false, in: `a: "a",    b: string `},
   105  
   106  		// Number typing (TODO)
   107  		//
   108  		// In principle, an "int" cannot assume an untyped "1", as "1" may
   109  		// still by typed as a float. They are two different type aspects. When
   110  		// considering, keep in mind that:
   111  		//   Key requirement: if A subsumes B, it must not be possible to
   112  		//   specialize B further such that A does not subsume B. HOWEVER,
   113  		//   The type conversion rules for conversion are INDEPENDENT of the
   114  		//   rules for subsumption!
   115  		// Consider:
   116  		// - only having number, but allowing user-defined types.
   117  		//   Subsumption would still work the same, but it may be somewhat
   118  		//   less weird.
   119  		// - making 1 always an int and 1.0 always a float.
   120  		//   - the int type would subsume any derived type from int.
   121  		//   - arithmetic would allow implicit conversions, but maybe not for
   122  		//     types.
   123  		//
   124  		// TODO: irrational numbers: allow untyped, but require explicit
   125  		//       trunking when assigning to float.
   126  		//
   127  		// a: number; cue.IsInteger(a) && a > 0
   128  		// t: (x) -> number; cue.IsInteger(a) && a > 0
   129  		// type x number: cue.IsInteger(x) && x > 0
   130  		// x: typeOf(number); cue.IsInteger(x) && x > 0
   131  		43: {subsumes: true, in: `a: 1, b: 1 `},
   132  		44: {subsumes: true, in: `a: 1.0, b: 1.0 `},
   133  		45: {subsumes: true, in: `a: 3.0, b: 3.0 `},
   134  		46: {subsumes: false, in: `a: 1.0, b: 1 `},
   135  		47: {subsumes: false, in: `a: 1, b: 1.0 `},
   136  		48: {subsumes: false, in: `a: 3, b: 3.0`},
   137  		49: {subsumes: true, in: `a: int, b: 1`},
   138  		50: {subsumes: true, in: `a: int, b: int & 1`},
   139  		51: {subsumes: true, in: `a: float, b: 1.0`},
   140  		52: {subsumes: false, in: `a: float, b: 1`},
   141  		53: {subsumes: false, in: `a: int, b: 1.0`},
   142  		54: {subsumes: true, in: `a: int, b: int`},
   143  		55: {subsumes: true, in: `a: number, b: int`},
   144  
   145  		// Structs
   146  		64: {subsumes: true, in: `a: {}, b: {}`},
   147  		65: {subsumes: true, in: `a: {}, b: {a: 1}`},
   148  		66: {subsumes: true, in: `a: {a:1}, b: {a:1, b:1}`},
   149  		67: {subsumes: true, in: `a: {s: { a:1} }, b: { s: { a:1, b:2 }}`},
   150  		68: {subsumes: true, in: `a: {}, b: {}`},
   151  		// TODO: allow subsumption of unevaluated values?
   152  		// ref not yet evaluated and not structurally equivalent
   153  		69: {subsumes: true, in: `a: {}, b: {} & c, c: {}`},
   154  
   155  		70: {subsumes: false, in: `a: {a:1}, b: {}`},
   156  		71: {subsumes: false, in: `a: {a:1, b:1}, b: {a:1}`},
   157  		72: {subsumes: false, in: `a: {s: { a:1} }, b: { s: {}}`},
   158  
   159  		84: {subsumes: true, in: `a: 1 | 2, b: 2 | 1`},
   160  		85: {subsumes: true, in: `a: 1 | 2, b: 1 | 2`},
   161  
   162  		86: {subsumes: true, in: `a: number, b: 2 | 1`},
   163  		87: {subsumes: true, in: `a: number, b: 2 | 1`},
   164  		88: {subsumes: false, in: `a: int, b: 1 | 2 | 3.1`},
   165  
   166  		89: {subsumes: true, in: `a: float | number, b: 1 | 2 | 3.1`},
   167  
   168  		90: {subsumes: false, in: `a: int, b: 1 | 2 | 3.1`},
   169  		91: {subsumes: true, in: `a: 1 | 2, b: 1`},
   170  		92: {subsumes: true, in: `a: 1 | 2, b: 2`},
   171  		93: {subsumes: false, in: `a: 1 | 2, b: 3`},
   172  
   173  		// 147: {subsumes: true, in: ` a: 7080, b: *7080 | int`, mode: subChoose},
   174  
   175  		// Defaults
   176  		150: {subsumes: false, in: `a: number | *1, b: number | *2`},
   177  		151: {subsumes: true, in: `a: number | *2, b: number | *2`},
   178  		152: {subsumes: true, in: `a: int | *float, b: int | *2.0`},
   179  		153: {subsumes: false, in: `a: int | *2, b: int | *2.0`},
   180  		154: {subsumes: true, in: `a: number | *2 | *3, b: number | *2`},
   181  		155: {subsumes: true, in: `a: number, b: number | *2`},
   182  
   183  		// Bounds
   184  		170: {subsumes: true, in: `a: >=2, b: >=2`},
   185  		171: {subsumes: true, in: `a: >=1, b: >=2`},
   186  		172: {subsumes: true, in: `a: >0, b: >=2`},
   187  		173: {subsumes: true, in: `a: >1, b: >1`},
   188  		174: {subsumes: true, in: `a: >=1, b: >1`},
   189  		175: {subsumes: false, in: `a: >1, b: >=1`},
   190  		176: {subsumes: true, in: `a: >=1, b: >=1`},
   191  		177: {subsumes: true, in: `a: <1, b: <1`},
   192  		178: {subsumes: true, in: `a: <=1, b: <1`},
   193  		179: {subsumes: false, in: `a: <1, b: <=1`},
   194  		180: {subsumes: true, in: `a: <=1, b: <=1`},
   195  
   196  		181: {subsumes: true, in: `a: !=1, b: !=1`},
   197  		182: {subsumes: false, in: `a: !=1, b: !=2`},
   198  
   199  		183: {subsumes: false, in: `a: !=1, b: <=1`},
   200  		184: {subsumes: true, in: `a: !=1, b: <1`},
   201  		185: {subsumes: false, in: `a: !=1, b: >=1`},
   202  		186: {subsumes: true, in: `a: !=1, b: <1`},
   203  
   204  		187: {subsumes: true, in: `a: !=1, b: <=0`},
   205  		188: {subsumes: true, in: `a: !=1, b: >=2`},
   206  		189: {subsumes: true, in: `a: !=1, b: >1`},
   207  
   208  		195: {subsumes: false, in: `a: >=2, b: !=2`},
   209  		196: {subsumes: false, in: `a: >2, b: !=2`},
   210  		197: {subsumes: false, in: `a: <2, b: !=2`},
   211  		198: {subsumes: false, in: `a: <=2, b: !=2`},
   212  
   213  		200: {subsumes: true, in: `a: =~"foo", b: =~"foo"`},
   214  		201: {subsumes: false, in: `a: =~"foo", b: =~"bar"`},
   215  		202: {subsumes: false, in: `a: =~"foo1", b: =~"foo"`},
   216  
   217  		203: {subsumes: true, in: `a: !~"foo", b: !~"foo"`},
   218  		204: {subsumes: false, in: `a: !~"foo", b: !~"bar"`},
   219  		205: {subsumes: false, in: `a: !~"foo", b: !~"foo1"`},
   220  
   221  		// The following is could be true, but we will not go down the rabbit
   222  		// hold of trying to prove subsumption of regular expressions.
   223  		210: {subsumes: false, in: `a: =~"foo", b: =~"foo1"`},
   224  		211: {subsumes: false, in: `a: !~"foo1", b: !~"foo"`},
   225  
   226  		220: {subsumes: true, in: `a: <5, b: 4`},
   227  		221: {subsumes: false, in: `a: <5, b: 5`},
   228  		222: {subsumes: true, in: `a: <=5, b: 5`},
   229  		223: {subsumes: false, in: `a: <=5.0, b: 5.00000001`},
   230  		224: {subsumes: true, in: `a: >5, b: 6`},
   231  		225: {subsumes: false, in: `a: >5, b: 5`},
   232  		226: {subsumes: true, in: `a: >=5, b: 5`},
   233  		227: {subsumes: false, in: `a: >=5, b: 4`},
   234  		228: {subsumes: true, in: `a: !=5, b: 6`},
   235  		229: {subsumes: false, in: `a: !=5, b: 5`},
   236  		230: {subsumes: false, in: `a: !=5.0, b: 5.0`},
   237  		231: {subsumes: false, in: `a: !=5.0, b: 5`},
   238  
   239  		250: {subsumes: true, in: `a: =~ #"^\d{3}$"#, b: "123"`},
   240  		251: {subsumes: false, in: `a: =~ #"^\d{3}$"#, b: "1234"`},
   241  		252: {subsumes: true, in: `a: !~ #"^\d{3}$"#, b: "1234"`},
   242  		253: {subsumes: false, in: `a: !~ #"^\d{3}$"#, b: "123"`},
   243  
   244  		// Conjunctions
   245  		300: {subsumes: true, in: `a: >0, b: >=2 & <=100`},
   246  		301: {subsumes: false, in: `a: >0, b: >=0 & <=100`},
   247  
   248  		310: {subsumes: true, in: `a: >=0 & <=100, b: 10`},
   249  		311: {subsumes: true, in: `a: >=0 & <=100, b: >=0 & <=100`},
   250  		312: {subsumes: false, in: `a: !=2 & !=4, b: >3`},
   251  		313: {subsumes: true, in: `a: !=2 & !=4, b: >5`},
   252  
   253  		314: {subsumes: false, in: `a: >=0 & <=100, b: >=0 & <=150`},
   254  		315: {subsumes: true, in: `a: >=0 & <=150, b: >=0 & <=100`},
   255  
   256  		// Disjunctions
   257  		330: {subsumes: true, in: `a: >5, b: >10 | 8`},
   258  		331: {subsumes: false, in: `a: >8, b: >10 | 8`},
   259  
   260  		// Optional fields
   261  		// Optional fields defined constraints on fields that are not yet
   262  		// defined. So even if such a field is not part of the output, it
   263  		// influences the lattice structure.
   264  		// For a given A and B, where A and B unify and where A has an optional
   265  		// field that is not defined in B, the addition of an incompatible
   266  		// value of that field in B can cause A and B to no longer unify.
   267  		//
   268  		400: {subsumes: false, in: `a: {foo: 1}, b: {}`},
   269  		401: {subsumes: false, in: `a: {foo?: 1}, b: {}`},
   270  		402: {subsumes: true, in: `a: {}, b: {foo: 1}`},
   271  		403: {subsumes: true, in: `a: {}, b: {foo?: 1}`},
   272  
   273  		404: {subsumes: true, in: `a: {foo: 1}, b: {foo: 1}`},
   274  		405: {subsumes: true, in: `a: {foo?: 1}, b: {foo: 1}`},
   275  		406: {subsumes: true, in: `a: {foo?: 1}, b: {foo?: 1}`},
   276  		407: {subsumes: false, in: `a: {foo: 1}, b: {foo?: 1}`},
   277  
   278  		408: {subsumes: false, in: `a: {foo: 1}, b: {foo: 2}`},
   279  		409: {subsumes: false, in: `a: {foo?: 1}, b: {foo: 2}`},
   280  		410: {subsumes: false, in: `a: {foo?: 1}, b: {foo?: 2}`},
   281  		411: {subsumes: false, in: `a: {foo: 1}, b: {foo?: 2}`},
   282  
   283  		412: {subsumes: true, in: `a: {foo: number}, b: {foo: 2}`},
   284  		413: {subsumes: true, in: `a: {foo?: number}, b: {foo: 2}`},
   285  		414: {subsumes: true, in: `a: {foo?: number}, b: {foo?: 2}`},
   286  		415: {subsumes: false, in: `a: {foo: number}, b: {foo?: 2}`},
   287  
   288  		416: {subsumes: false, in: `a: {foo: 1}, b: {foo: number}`},
   289  		417: {subsumes: false, in: `a: {foo?: 1}, b: {foo: number}`},
   290  		418: {subsumes: false, in: `a: {foo?: 1}, b: {foo?: number}`},
   291  		419: {subsumes: false, in: `a: {foo: 1}, b: {foo?: number}`},
   292  
   293  		// The one exception of the rule: there is no value of foo that can be
   294  		// added to b which would cause the unification of a and b to fail.
   295  		// So an optional field with a value of top is equivalent to not
   296  		// defining one at all.
   297  		420: {subsumes: true, in: `a: {foo?: _}, b: {}`},
   298  
   299  		430: {subsumes: false, in: `a: {[_]: 4}, b: {[_]: int}`},
   300  		431: {subsumes: true, in: `a: {[_]: int}, b: {[_]: 2}`, skip_v2: true},
   301  		432: {
   302  			subsumes: true,
   303  			in:       `a: {[string]: int, [<"m"]: 3}, b: {[string]: 2, [<"m"]: 3}`,
   304  			skip_v2:  true,
   305  		},
   306  		433: {
   307  			subsumes: true,
   308  			in:       `a: {[<"m"]: 3, [string]: int}, b: {[string]: 2, [<"m"]: 3}`,
   309  			skip_v2:  true,
   310  		},
   311  		434: {
   312  			subsumes: false,
   313  			in:       `a: {[<"n"]: 3, [string]: int}, b: {[string]: 2, [<"m"]: 3}`,
   314  		},
   315  		435: {
   316  			subsumes: true,
   317  			// both sides unify to a single string pattern.
   318  			in:      `a: {[string]: <5, [string]: int}, b: {[string]: <=3, [string]: 3}`,
   319  			skip_v2: true,
   320  		},
   321  		436: {
   322  			subsumes: true,
   323  			// matches because bottom is subsumed by >5
   324  			in:      `a: {[string]: >5}, b: {[string]: 1, [string]: 2}`,
   325  			skip_v2: true,
   326  		},
   327  		437: {
   328  			subsumes: false,
   329  			// subsumption gives up if a has more pattern constraints than b.
   330  			// TODO: support this?
   331  			in: `a: {[_]: >5, [>"b"]: int}, b: {[_]: 6}`,
   332  		},
   333  		438: {subsumes: true, in: `a: {}, b: {[_]: 6}`},
   334  
   335  		// TODO: the subNoOptional mode used to be used by the equality check.
   336  		// Now this has its own implementation it is no longer necessary. Keep
   337  		// around for now in case we still need the more permissive equality
   338  		// check that can be created by using subsumption.
   339  		//
   340  		// 440: {subsumes: true, in: `a: {foo?: 1}, b: {}`, mode: subNoOptional},
   341  		// 441: {subsumes: true, in: `a: {}, b: {foo?: 1}`, mode: subNoOptional},
   342  		// 442: {subsumes: true, in: `a: {foo?: 1}, b: {foo: 1}`, mode: subNoOptional},
   343  		// 443: {subsumes: true, in: `a: {foo?: 1}, b: {foo?: 1}`, mode: subNoOptional},
   344  		// 444: {subsumes: false, in: `a: {foo: 1}, b: {foo?: 1}`, mode: subNoOptional},
   345  		// 445: {subsumes: true, in: `a: close({}), b: {foo?: 1}`, mode: subNoOptional},
   346  		// 446: {subsumes: true, in: `a: close({}), b: close({foo?: 1})`, mode: subNoOptional},
   347  		// 447: {subsumes: true, in: `a: {}, b: close({})`, mode: subNoOptional},
   348  		// 448: {subsumes: true, in: `a: {}, b: close({foo?: 1})`, mode: subNoOptional},
   349  
   350  		// embedded scalars
   351  		460: {subsumes: true, in: `a: {1, #foo: number}, b: {1, #foo: 1}`},
   352  		461: {subsumes: true, in: `a: {1, #foo?: number}, b: {1, #foo: 1}`},
   353  		462: {subsumes: true, in: `a: {1, #foo?: number}, b: {1, #foo?: 1}`},
   354  		463: {subsumes: false, in: `a: {1, #foo: number}, b: {1, #foo?: 1}`},
   355  
   356  		464: {subsumes: true, in: `a: {int, #foo: number}, b: {1, #foo: 1}`},
   357  		465: {subsumes: false, in: `a: {int, #foo: 1}, b: {1, #foo: number}`},
   358  		466: {subsumes: false, in: `a: {1, #foo: number}, b: {int, #foo: 1}`},
   359  		467: {subsumes: false, in: `a: {1, #foo: 1}, b: {int, #foo: number}`},
   360  
   361  		// Lists
   362  		506: {subsumes: true, in: `a: [], b: [] `},
   363  		507: {subsumes: true, in: `a: [1], b: [1] `},
   364  		508: {subsumes: false, in: `a: [1], b: [2] `},
   365  		509: {subsumes: false, in: `a: [1], b: [2, 3] `},
   366  		510: {subsumes: true, in: `a: [{b: string}], b: [{b: "foo"}] `},
   367  		511: {subsumes: true, in: `a: [...{b: string}], b: [{b: "foo"}] `},
   368  		512: {subsumes: false, in: `a: [{b: "foo"}], b: [{b: string}] `},
   369  		513: {subsumes: false, in: `a: [{b: string}], b: [{b: "foo"}, ...{b: "foo"}] `},
   370  		520: {subsumes: false, in: `a: [_, int, ...], b: [int, string, ...string] `},
   371  
   372  		// Closed structs.
   373  		600: {subsumes: false, in: `a: close({}), b: {a: 1}`},
   374  		601: {subsumes: false, in: `a: close({a: 1}), b: {a: 1}`},
   375  		602: {subsumes: false, in: `a: close({a: 1, b: 1}), b: {a: 1}`},
   376  		603: {subsumes: false, in: `a: {a: 1}, b: close({})`},
   377  		604: {subsumes: true, in: `a: {a: 1}, b: close({a: 1})`},
   378  		605: {subsumes: true, in: `a: {a: 1}, b: close({a: 1, b: 1})`},
   379  		606: {subsumes: true, in: `a: close({b?: 1}), b: close({b: 1})`},
   380  		607: {subsumes: false, in: `a: close({b: 1}), b: close({b?: 1})`},
   381  		608: {subsumes: true, in: `a: {}, b: close({})`},
   382  		609: {subsumes: true, in: `a: {}, b: close({foo?: 1})`},
   383  		610: {subsumes: true, in: `a: {foo?:1}, b: close({})`},
   384  
   385  		// New in new evaluator.
   386  		611: {subsumes: false, in: `a: close({foo?:1}), b: close({bar?: 1})`},
   387  		612: {subsumes: true, in: `a: {foo?:1}, b: close({bar?: 1})`},
   388  		613: {subsumes: true, in: `a: {foo?:1}, b: close({bar: 1})`},
   389  
   390  		// Definitions are not regular fields.
   391  		630: {subsumes: false, in: `a: {#a: 1}, b: {a: 1}`},
   392  		631: {subsumes: false, in: `a: {a: 1}, b: {#a: 1}`},
   393  
   394  		// Subsuming final values.
   395  		700: {subsumes: true, in: `a: [string]: 1, b: {foo: 1}`, mode: subFinal},
   396  		701: {subsumes: true, in: `a: [string]: int, b: {foo: 1}`, mode: subFinal},
   397  		702: {subsumes: true, in: `a: {["foo"]: int}, b: {foo: 1}`, mode: subFinal},
   398  		703: {subsumes: false, in: `a: close({["foo"]: 1}), b: {bar: 1}`, mode: subFinal},
   399  		704: {subsumes: false, in: `a: {foo: 1}, b: {foo?: 1}`, mode: subFinal},
   400  		705: {subsumes: true, in: `a: close({}), b: {foo?: 1}`, mode: subFinal},
   401  		706: {subsumes: true, in: `a: close({}), b: close({foo?: 1})`, mode: subFinal},
   402  		707: {subsumes: true, in: `a: {}, b: close({})`, mode: subFinal},
   403  		708: {subsumes: false, in: `a: {[string]: 1}, b: {foo: 2}`, mode: subFinal},
   404  		709: {subsumes: true, in: `a: {}, b: close({foo?: 1})`, mode: subFinal},
   405  		710: {subsumes: false, in: `a: {foo: [...string]}, b: {}`, mode: subFinal},
   406  
   407  		// Schema values
   408  		800: {subsumes: true, in: `a: close({}), b: {foo: 1}`, mode: subSchema},
   409  		// TODO(eval): FIX
   410  		// 801: {subsumes: true, in: `a: {[string]: int}, b: {foo: 1}`, mode: subSchema},
   411  		804: {subsumes: false, in: `a: {foo: 1}, b: {foo?: 1}`, mode: subSchema},
   412  		805: {subsumes: true, in: `a: close({}), b: {foo?: 1}`, mode: subSchema},
   413  		806: {subsumes: true, in: `a: close({}), b: close({foo?: 1})`, mode: subSchema},
   414  		807: {subsumes: true, in: `a: {}, b: close({})`, mode: subSchema},
   415  		808: {subsumes: false, in: `a: {[string]: 1}, b: {foo: 2}`, mode: subSchema},
   416  		809: {subsumes: true, in: `a: {}, b: close({foo?: 1})`, mode: subSchema},
   417  
   418  		// Lists
   419  		950: {subsumes: true, in: `a: [], b: []`},
   420  		951: {subsumes: true, in: `a: [...], b: []`},
   421  		952: {subsumes: true, in: `a: [...], b: [...]`},
   422  		953: {subsumes: false, in: `a: [], b: [...]`},
   423  
   424  		954: {subsumes: true, in: `a: [2], b: [2]`},
   425  		955: {subsumes: true, in: `a: [int], b: [2]`},
   426  		956: {subsumes: false, in: `a: [2], b: [int]`},
   427  		957: {subsumes: true, in: `a: [int], b: [int]`},
   428  
   429  		958: {subsumes: true, in: `a: [...2], b: [2]`},
   430  		959: {subsumes: true, in: `a: [...int], b: [2]`},
   431  		960: {subsumes: false, in: `a: [...2], b: [int]`},
   432  		961: {subsumes: true, in: `a: [...int], b: [int]`},
   433  
   434  		962: {subsumes: false, in: `a: [2], b: [...2]`},
   435  		963: {subsumes: false, in: `a: [int], b: [...2]`},
   436  		964: {subsumes: false, in: `a: [2], b: [...int]`},
   437  		965: {subsumes: false, in: `a: [int], b: [...int]`},
   438  
   439  		966: {subsumes: false, in: `a: [...int], b: ["foo"]`},
   440  		967: {subsumes: false, in: `a: ["foo"], b: [...int]`},
   441  
   442  		// Defaults:
   443  		// TODO: for the purpose of v0.2 compatibility these
   444  		// evaluate to true. Reconsider before making this package
   445  		// public.
   446  		970: {subsumes: true, in: `a: [], b: [...int]`, mode: subDefaults},
   447  		971: {subsumes: true, in: `a: [2], b: [2, ...int]`, mode: subDefaults},
   448  
   449  		// Final
   450  		980: {subsumes: true, in: `a: [], b: [...int]`, mode: subFinal},
   451  		981: {subsumes: true, in: `a: [2], b: [2, ...int]`, mode: subFinal},
   452  	}
   453  
   454  	re := regexp.MustCompile(`a: (.*).*b: ([^\n]*)`)
   455  	for i, tc := range testCases {
   456  		if tc.in == "" {
   457  			continue
   458  		}
   459  		m := re.FindStringSubmatch(strings.Join(strings.Split(tc.in, "\n"), ""))
   460  		const cutset = "\n ,"
   461  		key := strings.Trim(m[1], cutset) + " ⊑ " + strings.Trim(m[2], cutset)
   462  
   463  		cuetdtest.FullMatrix.Run(t, strconv.Itoa(i)+"/"+key, func(t *cuetdtest.M) {
   464  			if tc.skip_v2 && t.IsDefault() {
   465  				t.Skipf("skipping v2 test")
   466  			}
   467  			r := t.Runtime()
   468  
   469  			file, err := parser.ParseFile("subsume", tc.in)
   470  			if err != nil {
   471  				t.Fatal(err)
   472  			}
   473  
   474  			root, errs := compile.Files(nil, r, "", file)
   475  			if errs != nil {
   476  				t.Fatal(errs)
   477  			}
   478  
   479  			ctx := eval.NewContext(r, root)
   480  			root.Finalize(ctx)
   481  
   482  			// Use low-level lookup to avoid evaluation.
   483  			var a, b adt.Value
   484  			for _, arc := range root.Arcs {
   485  				switch arc.Label {
   486  				case ctx.StringLabel("a"):
   487  					a = arc
   488  				case ctx.StringLabel("b"):
   489  					b = arc
   490  				}
   491  			}
   492  
   493  			switch tc.mode {
   494  			case subNone:
   495  				err = subsume.Value(ctx, a, b)
   496  			case subSchema:
   497  				err = subsume.API.Value(ctx, a, b)
   498  			// TODO: see comments above.
   499  			// case subNoOptional:
   500  			// 	err = IgnoreOptional.Value(ctx, a, b)
   501  			case subDefaults:
   502  				p := subsume.Profile{Defaults: true}
   503  				err = p.Value(ctx, a, b)
   504  			case subFinal:
   505  				err = subsume.Final.Value(ctx, a, b)
   506  			}
   507  			got := err == nil
   508  
   509  			if got != tc.subsumes {
   510  				t.Errorf("got %v; want %v (%v vs %v)", got, tc.subsumes, a.Kind(), b.Kind())
   511  			}
   512  		})
   513  	}
   514  }