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