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