kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/go/serving/xrefs/columnar_test.go (about) 1 /* 2 * Copyright 2018 The Kythe Authors. All rights reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package xrefs 18 19 import ( 20 "context" 21 "testing" 22 23 "kythe.io/kythe/go/services/xrefs" 24 "kythe.io/kythe/go/serving/xrefs/columnar" 25 "kythe.io/kythe/go/storage/inmemory" 26 "kythe.io/kythe/go/storage/keyvalue" 27 "kythe.io/kythe/go/util/compare" 28 "kythe.io/kythe/go/util/kytheuri" 29 "kythe.io/kythe/go/util/schema/facts" 30 31 cpb "kythe.io/kythe/proto/common_go_proto" 32 scpb "kythe.io/kythe/proto/schema_go_proto" 33 srvpb "kythe.io/kythe/proto/serving_go_proto" 34 spb "kythe.io/kythe/proto/storage_go_proto" 35 xpb "kythe.io/kythe/proto/xref_go_proto" 36 xspb "kythe.io/kythe/proto/xref_serving_go_proto" 37 ) 38 39 func mustWriteDecor(t *testing.T, w keyvalue.Writer, fd *xspb.FileDecorations) { 40 kv, err := columnar.EncodeDecorationsEntry(columnar.DecorationsKeyPrefix, fd) 41 if err != nil { 42 t.Fatal(err) 43 } 44 mustWrite(t, w, kv.Key, kv.Value) 45 } 46 47 func mustWriteXRef(t *testing.T, w keyvalue.Writer, fd *xspb.CrossReferences) { 48 kv, err := columnar.EncodeCrossReferencesEntry(columnar.CrossReferencesKeyPrefix, fd) 49 if err != nil { 50 t.Fatal(err) 51 } 52 mustWrite(t, w, kv.Key, kv.Value) 53 } 54 55 func mustWrite(t *testing.T, w keyvalue.Writer, key, val []byte) { 56 if err := w.Write(key, val); err != nil { 57 t.Fatal(err) 58 } 59 } 60 61 func TestServingDecorations(t *testing.T) { 62 ctx := context.Background() 63 db := inmemory.NewKeyValueDB() 64 w, err := db.Writer(ctx) 65 if err != nil { 66 t.Fatal(err) 67 } 68 69 // Mark table as columnar 70 mustWrite(t, w, []byte(ColumnarTableKeyMarker), []byte{}) 71 72 file := &spb.VName{Path: "path"} 73 const expectedText = "some text\n" 74 decor := []*xspb.FileDecorations{{ 75 File: file, 76 Entry: &xspb.FileDecorations_Index_{&xspb.FileDecorations_Index{ 77 TextEncoding: "ascii", 78 }}, 79 }, { 80 File: file, 81 Entry: &xspb.FileDecorations_Text_{&xspb.FileDecorations_Text{ 82 Text: []byte(expectedText), 83 }}, 84 }, { 85 File: file, 86 Entry: &xspb.FileDecorations_Target_{&xspb.FileDecorations_Target{ 87 StartOffset: 0, 88 EndOffset: 4, 89 Kind: &xspb.FileDecorations_Target_KytheKind{ 90 scpb.EdgeKind_REF, 91 }, 92 Target: &spb.VName{Signature: "simpleDecor"}, 93 }}, 94 }, { 95 File: file, 96 Entry: &xspb.FileDecorations_TargetNode_{&xspb.FileDecorations_TargetNode{ 97 Node: &scpb.Node{ 98 Source: &spb.VName{Signature: "simpleDecor"}, 99 Kind: &scpb.Node_KytheKind{scpb.NodeKind_RECORD}, 100 }, 101 }}, 102 }, { 103 File: file, 104 Entry: &xspb.FileDecorations_Target_{&xspb.FileDecorations_Target{ 105 StartOffset: 5, 106 EndOffset: 9, 107 Kind: &xspb.FileDecorations_Target_KytheKind{ 108 scpb.EdgeKind_REF, 109 }, 110 Target: &spb.VName{Signature: "decorWithDef"}, 111 }}, 112 }, { 113 File: file, 114 Entry: &xspb.FileDecorations_TargetDefinition_{&xspb.FileDecorations_TargetDefinition{ 115 Target: &spb.VName{Signature: "decorWithDef"}, 116 Definition: &spb.VName{Signature: "def1"}, 117 }}, 118 }, { 119 File: file, 120 Entry: &xspb.FileDecorations_DefinitionLocation_{&xspb.FileDecorations_DefinitionLocation{ 121 Location: &srvpb.ExpandedAnchor{ 122 Ticket: "kythe:#def1", 123 }, 124 }}, 125 }} 126 for _, fd := range decor { 127 mustWriteDecor(t, w, fd) 128 } 129 130 if err := w.Close(); err != nil { 131 t.Fatal(err) 132 } 133 134 xs := NewService(ctx, db) 135 136 fileTicket := kytheuri.ToString(file) 137 t.Run("file_location", func(t *testing.T) { 138 reply, err := xs.Decorations(ctx, &xpb.DecorationsRequest{ 139 Location: &xpb.Location{Ticket: fileTicket}, 140 SourceText: true, 141 }) 142 if err != nil { 143 t.Fatalf("Decorations error: %v", err) 144 } 145 146 expectedLoc := &xpb.Location{Ticket: fileTicket} 147 if diff := compare.ProtoDiff(expectedLoc, reply.Location); diff != "" { 148 t.Fatalf("Location differences: (- expected; + found)\n%s", diff) 149 } 150 }) 151 152 t.Run("source_text", makeDecorTestCase(ctx, xs, &xpb.DecorationsRequest{ 153 Location: &xpb.Location{Ticket: fileTicket}, 154 SourceText: true, 155 }, &xpb.DecorationsReply{ 156 Location: &xpb.Location{Ticket: fileTicket}, 157 SourceText: []byte(expectedText), 158 Encoding: "ascii", 159 })) 160 161 t.Run("references", makeDecorTestCase(ctx, xs, &xpb.DecorationsRequest{ 162 Location: &xpb.Location{Ticket: fileTicket}, 163 References: true, 164 }, &xpb.DecorationsReply{ 165 Location: &xpb.Location{Ticket: fileTicket}, 166 Reference: []*xpb.DecorationsReply_Reference{{ 167 Span: &cpb.Span{ 168 Start: &cpb.Point{ 169 LineNumber: 1, 170 }, 171 End: &cpb.Point{ 172 ByteOffset: 4, 173 ColumnOffset: 4, 174 LineNumber: 1, 175 }, 176 }, 177 Kind: "/kythe/edge/ref", 178 TargetTicket: "kythe:#simpleDecor", 179 }, { 180 Span: &cpb.Span{ 181 Start: &cpb.Point{ 182 ByteOffset: 5, 183 ColumnOffset: 5, 184 LineNumber: 1, 185 }, 186 End: &cpb.Point{ 187 ByteOffset: 9, 188 ColumnOffset: 9, 189 LineNumber: 1, 190 }, 191 }, 192 Kind: "/kythe/edge/ref", 193 TargetTicket: "kythe:#decorWithDef", 194 // TargetDefinition: explicitly not requested 195 }}, 196 // Nodes: not requested 197 // DefinitionLocations: not requested 198 })) 199 200 t.Run("references_dirty", makeDecorTestCase(ctx, xs, &xpb.DecorationsRequest{ 201 Location: &xpb.Location{Ticket: fileTicket}, 202 References: true, 203 DirtyBuffer: []byte("\n " + expectedText), 204 }, &xpb.DecorationsReply{ 205 Location: &xpb.Location{Ticket: fileTicket}, 206 Reference: []*xpb.DecorationsReply_Reference{{ 207 Span: &cpb.Span{ 208 Start: &cpb.Point{ 209 ByteOffset: 0 + 2, 210 ColumnOffset: 0 + 1, 211 LineNumber: 1 + 1, 212 }, 213 End: &cpb.Point{ 214 ByteOffset: 4 + 2, 215 ColumnOffset: 4 + 1, 216 LineNumber: 1 + 1, 217 }, 218 }, 219 Kind: "/kythe/edge/ref", 220 TargetTicket: "kythe:#simpleDecor", 221 }, { 222 Span: &cpb.Span{ 223 Start: &cpb.Point{ 224 ByteOffset: 5 + 2, 225 ColumnOffset: 5 + 1, 226 LineNumber: 1 + 1, 227 }, 228 End: &cpb.Point{ 229 ByteOffset: 9 + 2, 230 ColumnOffset: 9 + 1, 231 LineNumber: 1 + 1, 232 }, 233 }, 234 Kind: "/kythe/edge/ref", 235 TargetTicket: "kythe:#decorWithDef", 236 // TargetDefinition: explicitly not requested 237 }}, 238 // Nodes: not requested 239 // DefinitionLocations: not requested 240 })) 241 242 t.Run("referenced_nodes", makeDecorTestCase(ctx, xs, &xpb.DecorationsRequest{ 243 Location: &xpb.Location{Ticket: fileTicket}, 244 References: true, 245 Filter: []string{"**"}, 246 }, &xpb.DecorationsReply{ 247 Location: &xpb.Location{Ticket: fileTicket}, 248 Reference: []*xpb.DecorationsReply_Reference{{ 249 Span: &cpb.Span{ 250 Start: &cpb.Point{ 251 LineNumber: 1, 252 }, 253 End: &cpb.Point{ 254 ByteOffset: 4, 255 ColumnOffset: 4, 256 LineNumber: 1, 257 }, 258 }, 259 Kind: "/kythe/edge/ref", 260 TargetTicket: "kythe:#simpleDecor", 261 }, { 262 Span: &cpb.Span{ 263 Start: &cpb.Point{ 264 ByteOffset: 5, 265 ColumnOffset: 5, 266 LineNumber: 1, 267 }, 268 End: &cpb.Point{ 269 ByteOffset: 9, 270 ColumnOffset: 9, 271 LineNumber: 1, 272 }, 273 }, 274 Kind: "/kythe/edge/ref", 275 TargetTicket: "kythe:#decorWithDef", 276 // TargetDefinition: explicitly not requested 277 }}, 278 Nodes: map[string]*cpb.NodeInfo{ 279 "kythe:#simpleDecor": { 280 Facts: map[string][]byte{ 281 "/kythe/node/kind": []byte("record"), 282 }, 283 }, 284 }, 285 // DefinitionLocations: not requested 286 })) 287 288 t.Run("target_definitions", makeDecorTestCase(ctx, xs, &xpb.DecorationsRequest{ 289 Location: &xpb.Location{Ticket: fileTicket}, 290 References: true, 291 TargetDefinitions: true, 292 }, &xpb.DecorationsReply{ 293 Location: &xpb.Location{Ticket: fileTicket}, 294 Reference: []*xpb.DecorationsReply_Reference{{ 295 Span: &cpb.Span{ 296 Start: &cpb.Point{ 297 LineNumber: 1, 298 }, 299 End: &cpb.Point{ 300 ByteOffset: 4, 301 ColumnOffset: 4, 302 LineNumber: 1, 303 }, 304 }, 305 Kind: "/kythe/edge/ref", 306 TargetTicket: "kythe:#simpleDecor", 307 }, { 308 Span: &cpb.Span{ 309 Start: &cpb.Point{ 310 ByteOffset: 5, 311 ColumnOffset: 5, 312 LineNumber: 1, 313 }, 314 End: &cpb.Point{ 315 ByteOffset: 9, 316 ColumnOffset: 9, 317 LineNumber: 1, 318 }, 319 }, 320 Kind: "/kythe/edge/ref", 321 TargetTicket: "kythe:#decorWithDef", 322 TargetDefinition: "kythe:#def1", // expected definition 323 }}, 324 // Nodes: not requested 325 DefinitionLocations: map[string]*xpb.Anchor{ 326 "kythe:#def1": { 327 Ticket: "kythe:#def1", 328 Parent: "kythe:", 329 }, 330 }, 331 })) 332 333 // TODO(schroederc): test split file contents 334 // TODO(schroederc): test overrides 335 // TODO(schroederc): test diagnostics (w/ or w/o span) 336 } 337 338 func makeDecorTestCase(ctx context.Context, xs xrefs.Service, req *xpb.DecorationsRequest, expected *xpb.DecorationsReply) func(*testing.T) { 339 return func(t *testing.T) { 340 reply, err := xs.Decorations(ctx, req) 341 if err != nil { 342 t.Fatalf("Decorations error: %v", err) 343 } 344 if diff := compare.ProtoDiff(expected, reply); diff != "" { 345 t.Fatalf("DecorationsReply differences: (- expected; + found)\n%s", diff) 346 } 347 } 348 } 349 350 func TestServingCrossReferences(t *testing.T) { 351 ctx := context.Background() 352 db := inmemory.NewKeyValueDB() 353 w, err := db.Writer(ctx) 354 if err != nil { 355 t.Fatal(err) 356 } 357 358 // Mark table as columnar 359 mustWrite(t, w, []byte(ColumnarTableKeyMarker), []byte{}) 360 361 src := &spb.VName{Path: "path", Signature: "signature"} 362 span := &cpb.Span{ 363 Start: &cpb.Point{ 364 ByteOffset: 5, 365 ColumnOffset: 5, 366 LineNumber: 1, 367 }, 368 End: &cpb.Point{ 369 ByteOffset: 9, 370 ColumnOffset: 9, 371 LineNumber: 1, 372 }, 373 } 374 ms := &cpb.MarkedSource{ 375 Kind: cpb.MarkedSource_IDENTIFIER, 376 PreText: "identifier", 377 } 378 xrefs := []*xspb.CrossReferences{{ 379 Source: src, 380 Entry: &xspb.CrossReferences_Index_{&xspb.CrossReferences_Index{ 381 Node: &scpb.Node{ 382 Kind: &scpb.Node_KytheKind{scpb.NodeKind_RECORD}, 383 }, 384 MarkedSource: ms, 385 }}, 386 }, { 387 Source: src, 388 Entry: &xspb.CrossReferences_Reference_{&xspb.CrossReferences_Reference{ 389 Kind: &xspb.CrossReferences_Reference_KytheKind{scpb.EdgeKind_REF}, 390 Location: &srvpb.ExpandedAnchor{ 391 Ticket: "kythe:?path=path1#ref1", 392 Span: span, 393 }, 394 }}, 395 }, { 396 Source: src, 397 Entry: &xspb.CrossReferences_Reference_{&xspb.CrossReferences_Reference{ 398 Kind: &xspb.CrossReferences_Reference_KytheKind{scpb.EdgeKind_REF_CALL}, 399 Location: &srvpb.ExpandedAnchor{ 400 Ticket: "kythe:?path=path2#ref2", 401 Span: span, 402 }, 403 }}, 404 }, { 405 Source: src, 406 Entry: &xspb.CrossReferences_Reference_{&xspb.CrossReferences_Reference{ 407 Kind: &xspb.CrossReferences_Reference_KytheKind{scpb.EdgeKind_DEFINES}, 408 Location: &srvpb.ExpandedAnchor{ 409 Ticket: "kythe:?path=path1#def1", 410 Span: span, 411 }, 412 }}, 413 }, { 414 Source: src, 415 Entry: &xspb.CrossReferences_Reference_{&xspb.CrossReferences_Reference{ 416 Kind: &xspb.CrossReferences_Reference_KytheKind{scpb.EdgeKind_DEFINES_BINDING}, 417 Location: &srvpb.ExpandedAnchor{ 418 Ticket: "kythe:?path=path2#def2", 419 Span: span, 420 }, 421 }}, 422 }, { 423 Source: src, 424 Entry: &xspb.CrossReferences_Reference_{&xspb.CrossReferences_Reference{ 425 Kind: &xspb.CrossReferences_Reference_GenericKind{"#internal/ref/declare"}, 426 Location: &srvpb.ExpandedAnchor{ 427 Ticket: "kythe:?path=path1#decl1", 428 Span: span, 429 }, 430 }}, 431 }, { 432 Source: src, 433 Entry: &xspb.CrossReferences_Reference_{&xspb.CrossReferences_Reference{ 434 Kind: &xspb.CrossReferences_Reference_GenericKind{"#internal/ref/declare"}, 435 Location: &srvpb.ExpandedAnchor{ 436 Ticket: "kythe:?path=path2#decl2", 437 Span: span, 438 }, 439 }}, 440 }, { 441 Source: src, 442 Entry: &xspb.CrossReferences_Caller_{&xspb.CrossReferences_Caller{ 443 Caller: &spb.VName{Signature: "caller"}, 444 Location: &srvpb.ExpandedAnchor{ 445 Ticket: "kythe:?path=path#caller", 446 Span: span, 447 }, 448 MarkedSource: &cpb.MarkedSource{Kind: cpb.MarkedSource_IDENTIFIER, PreText: "caller"}, 449 }}, 450 }, { 451 Source: src, 452 Entry: &xspb.CrossReferences_Callsite_{&xspb.CrossReferences_Callsite{ 453 Caller: &spb.VName{Signature: "caller"}, 454 Kind: xspb.CrossReferences_Callsite_DIRECT, 455 Location: &srvpb.ExpandedAnchor{ 456 Ticket: "kythe:?path=path2#callsite", 457 Span: span, 458 }, 459 }}, 460 }, { 461 Source: src, 462 Entry: &xspb.CrossReferences_Callsite_{&xspb.CrossReferences_Callsite{ 463 Caller: &spb.VName{Signature: "caller"}, 464 Kind: xspb.CrossReferences_Callsite_OVERRIDE, 465 Location: &srvpb.ExpandedAnchor{ 466 Ticket: "kythe:?path=path3#callsite_override", 467 Span: span, 468 }, 469 }}, 470 }, { 471 Source: src, 472 Entry: &xspb.CrossReferences_Relation_{&xspb.CrossReferences_Relation{ 473 Node: &spb.VName{Signature: "relatedNode"}, 474 Kind: &xspb.CrossReferences_Relation_KytheKind{scpb.EdgeKind_CHILD_OF}, 475 Reverse: true, 476 }}, 477 }, { 478 Source: src, 479 Entry: &xspb.CrossReferences_RelatedNode_{&xspb.CrossReferences_RelatedNode{ 480 Node: &scpb.Node{ 481 Source: &spb.VName{Signature: "relatedNode"}, 482 Kind: &scpb.Node_KytheKind{scpb.NodeKind_FUNCTION}, 483 }, 484 }}, 485 }, { 486 Source: src, 487 Entry: &xspb.CrossReferences_NodeDefinition_{&xspb.CrossReferences_NodeDefinition{ 488 Node: &spb.VName{Signature: "relatedNode"}, 489 Location: &srvpb.ExpandedAnchor{ 490 Ticket: "kythe:#relatedNodeDef", 491 Span: span, 492 }, 493 }}, 494 }} 495 for _, xr := range xrefs { 496 mustWriteXRef(t, w, xr) 497 } 498 if err := w.Close(); err != nil { 499 t.Fatal(err) 500 } 501 xs := NewService(ctx, db) 502 503 refs := []*xpb.CrossReferencesReply_RelatedAnchor{{ 504 Anchor: &xpb.Anchor{ 505 Parent: "kythe:?path=path1", 506 Span: span, 507 }, 508 }, { 509 Anchor: &xpb.Anchor{ 510 Parent: "kythe:?path=path2", 511 Span: span, 512 }, 513 }} 514 515 defs := []*xpb.CrossReferencesReply_RelatedAnchor{{ 516 Anchor: &xpb.Anchor{ 517 Parent: "kythe:?path=path1", 518 Span: span, 519 }, 520 }, { 521 Anchor: &xpb.Anchor{ 522 Parent: "kythe:?path=path2", 523 Span: span, 524 }, 525 }} 526 527 decls := []*xpb.CrossReferencesReply_RelatedAnchor{{ 528 Anchor: &xpb.Anchor{ 529 Parent: "kythe:?path=path1", 530 Span: span, 531 }, 532 }, { 533 Anchor: &xpb.Anchor{ 534 Parent: "kythe:?path=path2", 535 Span: span, 536 }, 537 }} 538 539 ticket := kytheuri.ToString(src) 540 541 t.Run("requested_node", makeXRefTestCase(ctx, xs, &xpb.CrossReferencesRequest{ 542 Ticket: []string{ticket}, 543 Filter: []string{"**"}, 544 RelatedNodeKind: []string{"NONE"}, 545 }, &xpb.CrossReferencesReply{ 546 CrossReferences: map[string]*xpb.CrossReferencesReply_CrossReferenceSet{ 547 ticket: { 548 Ticket: ticket, 549 MarkedSource: ms, 550 }, 551 }, 552 Nodes: map[string]*cpb.NodeInfo{ 553 ticket: { 554 Facts: map[string][]byte{ 555 "/kythe/node/kind": []byte("record"), 556 }, 557 }, 558 }, 559 })) 560 561 t.Run("refs", makeXRefTestCase(ctx, xs, &xpb.CrossReferencesRequest{ 562 Ticket: []string{ticket}, 563 ReferenceKind: xpb.CrossReferencesRequest_ALL_REFERENCES, 564 }, &xpb.CrossReferencesReply{ 565 CrossReferences: map[string]*xpb.CrossReferencesReply_CrossReferenceSet{ 566 ticket: { 567 Ticket: ticket, 568 MarkedSource: ms, 569 Reference: refs, 570 }, 571 }, 572 })) 573 574 t.Run("decls", makeXRefTestCase(ctx, xs, &xpb.CrossReferencesRequest{ 575 Ticket: []string{ticket}, 576 DeclarationKind: xpb.CrossReferencesRequest_ALL_DECLARATIONS, 577 }, &xpb.CrossReferencesReply{ 578 CrossReferences: map[string]*xpb.CrossReferencesReply_CrossReferenceSet{ 579 ticket: { 580 Ticket: ticket, 581 MarkedSource: ms, 582 Declaration: decls, 583 }, 584 }, 585 })) 586 587 t.Run("defs", makeXRefTestCase(ctx, xs, &xpb.CrossReferencesRequest{ 588 Ticket: []string{ticket}, 589 DefinitionKind: xpb.CrossReferencesRequest_ALL_DEFINITIONS, 590 }, &xpb.CrossReferencesReply{ 591 CrossReferences: map[string]*xpb.CrossReferencesReply_CrossReferenceSet{ 592 ticket: { 593 Ticket: ticket, 594 MarkedSource: ms, 595 Definition: defs, 596 }, 597 }, 598 })) 599 600 t.Run("full_defs", makeXRefTestCase(ctx, xs, &xpb.CrossReferencesRequest{ 601 Ticket: []string{ticket}, 602 DefinitionKind: xpb.CrossReferencesRequest_FULL_DEFINITIONS, 603 }, &xpb.CrossReferencesReply{ 604 CrossReferences: map[string]*xpb.CrossReferencesReply_CrossReferenceSet{ 605 ticket: { 606 Ticket: ticket, 607 MarkedSource: ms, 608 Definition: defs[0:1], 609 }, 610 }, 611 })) 612 613 t.Run("bindings", makeXRefTestCase(ctx, xs, &xpb.CrossReferencesRequest{ 614 Ticket: []string{ticket}, 615 DefinitionKind: xpb.CrossReferencesRequest_BINDING_DEFINITIONS, 616 }, &xpb.CrossReferencesReply{ 617 CrossReferences: map[string]*xpb.CrossReferencesReply_CrossReferenceSet{ 618 ticket: { 619 Ticket: ticket, 620 MarkedSource: ms, 621 Definition: defs[1:2], 622 }, 623 }, 624 })) 625 626 t.Run("related_nodes", makeXRefTestCase(ctx, xs, &xpb.CrossReferencesRequest{ 627 Ticket: []string{ticket}, 628 ReferenceKind: xpb.CrossReferencesRequest_ALL_REFERENCES, 629 Filter: []string{"**"}, 630 }, &xpb.CrossReferencesReply{ 631 CrossReferences: map[string]*xpb.CrossReferencesReply_CrossReferenceSet{ 632 ticket: { 633 Ticket: ticket, 634 MarkedSource: ms, 635 Reference: refs, 636 RelatedNode: []*xpb.CrossReferencesReply_RelatedNode{{ 637 Ticket: "kythe:#relatedNode", 638 RelationKind: "%/kythe/edge/childof", 639 }}, 640 }, 641 }, 642 Nodes: map[string]*cpb.NodeInfo{ 643 ticket: { 644 Facts: map[string][]byte{ 645 "/kythe/node/kind": []byte("record"), 646 }, 647 }, 648 "kythe:#relatedNode": { 649 Facts: map[string][]byte{ 650 "/kythe/node/kind": []byte("function"), 651 }, 652 }, 653 }, 654 })) 655 656 t.Run("node_definitions", makeXRefTestCase(ctx, xs, &xpb.CrossReferencesRequest{ 657 Ticket: []string{ticket}, 658 Filter: []string{facts.NodeKind}, 659 NodeDefinitions: true, 660 }, &xpb.CrossReferencesReply{ 661 CrossReferences: map[string]*xpb.CrossReferencesReply_CrossReferenceSet{ 662 ticket: { 663 Ticket: ticket, 664 MarkedSource: ms, 665 RelatedNode: []*xpb.CrossReferencesReply_RelatedNode{{ 666 Ticket: "kythe:#relatedNode", 667 RelationKind: "%/kythe/edge/childof", 668 }}, 669 }, 670 }, 671 Nodes: map[string]*cpb.NodeInfo{ 672 ticket: {Facts: map[string][]byte{"/kythe/node/kind": []byte("record")}}, 673 "kythe:#relatedNode": { 674 Facts: map[string][]byte{"/kythe/node/kind": []byte("function")}, 675 Definition: "kythe:#relatedNodeDef", 676 }, 677 }, 678 DefinitionLocations: map[string]*xpb.Anchor{ 679 "kythe:#relatedNodeDef": { 680 Ticket: "kythe:#relatedNodeDef", 681 Parent: "kythe:", 682 Span: span, 683 }, 684 }, 685 })) 686 687 t.Run("non_call_refs", makeXRefTestCase(ctx, xs, &xpb.CrossReferencesRequest{ 688 Ticket: []string{ticket}, 689 ReferenceKind: xpb.CrossReferencesRequest_NON_CALL_REFERENCES, 690 }, &xpb.CrossReferencesReply{ 691 CrossReferences: map[string]*xpb.CrossReferencesReply_CrossReferenceSet{ 692 ticket: { 693 Ticket: ticket, 694 MarkedSource: ms, 695 Reference: refs[0:1], 696 }, 697 }, 698 })) 699 700 t.Run("callsites", makeXRefTestCase(ctx, xs, &xpb.CrossReferencesRequest{ 701 Ticket: []string{ticket}, 702 CallerKind: xpb.CrossReferencesRequest_OVERRIDE_CALLERS, 703 }, &xpb.CrossReferencesReply{ 704 CrossReferences: map[string]*xpb.CrossReferencesReply_CrossReferenceSet{ 705 ticket: { 706 Ticket: ticket, 707 MarkedSource: ms, 708 Caller: []*xpb.CrossReferencesReply_RelatedAnchor{{ 709 Ticket: "kythe:#caller", 710 Anchor: &xpb.Anchor{ 711 Parent: "kythe:?path=path", 712 Span: span, 713 }, 714 MarkedSource: &cpb.MarkedSource{Kind: cpb.MarkedSource_IDENTIFIER, PreText: "caller"}, 715 Site: []*xpb.Anchor{{ 716 Parent: "kythe:?path=path2", 717 Span: span, 718 }, { 719 Parent: "kythe:?path=path3", 720 Span: span, 721 }}, 722 }}, 723 }, 724 }, 725 })) 726 727 t.Run("direct_callsites", makeXRefTestCase(ctx, xs, &xpb.CrossReferencesRequest{ 728 Ticket: []string{ticket}, 729 CallerKind: xpb.CrossReferencesRequest_DIRECT_CALLERS, 730 }, &xpb.CrossReferencesReply{ 731 CrossReferences: map[string]*xpb.CrossReferencesReply_CrossReferenceSet{ 732 ticket: { 733 Ticket: ticket, 734 MarkedSource: ms, 735 Caller: []*xpb.CrossReferencesReply_RelatedAnchor{{ 736 Ticket: "kythe:#caller", 737 Anchor: &xpb.Anchor{ 738 Parent: "kythe:?path=path", 739 Span: span, 740 }, 741 MarkedSource: &cpb.MarkedSource{Kind: cpb.MarkedSource_IDENTIFIER, PreText: "caller"}, 742 Site: []*xpb.Anchor{{ 743 Parent: "kythe:?path=path2", 744 Span: span, 745 }}, 746 }}, 747 }, 748 }, 749 })) 750 } 751 752 func makeXRefTestCase(ctx context.Context, xs xrefs.Service, req *xpb.CrossReferencesRequest, expected *xpb.CrossReferencesReply) func(*testing.T) { 753 return func(t *testing.T) { 754 reply, err := xs.CrossReferences(ctx, req) 755 if err != nil { 756 t.Fatalf("CrossReferences error: %v", err) 757 } 758 if diff := compare.ProtoDiff(expected, reply); diff != "" { 759 t.Fatalf("CrossReferencesReply differences: (- expected; + found)\n%s", diff) 760 } 761 } 762 }