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