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 }