kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/go/serving/xrefs/xrefs_test.go (about) 1 /* 2 * Copyright 2015 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 "bytes" 21 "context" 22 "flag" 23 "math" 24 "math/rand" 25 "reflect" 26 "sort" 27 "strconv" 28 "testing" 29 "testing/quick" 30 31 "bitbucket.org/creachadair/stringset" 32 "github.com/google/codesearch/index" 33 "kythe.io/kythe/go/services/xrefs" 34 "kythe.io/kythe/go/storage/table" 35 "kythe.io/kythe/go/test/testutil" 36 "kythe.io/kythe/go/util/compare" 37 "kythe.io/kythe/go/util/kytheuri" 38 "kythe.io/kythe/go/util/span" 39 40 "golang.org/x/text/encoding" 41 "golang.org/x/text/encoding/unicode" 42 "golang.org/x/text/transform" 43 "google.golang.org/protobuf/encoding/prototext" 44 "google.golang.org/protobuf/proto" 45 46 cpb "kythe.io/kythe/proto/common_go_proto" 47 srvpb "kythe.io/kythe/proto/serving_go_proto" 48 xpb "kythe.io/kythe/proto/xref_go_proto" 49 ) 50 51 var ( 52 ctx = context.Background() 53 54 nodes = []*srvpb.Node{ 55 { 56 Ticket: "kythe://someCorpus?lang=otpl#signature", 57 Fact: makeFactList("/kythe/node/kind", "testNode"), 58 }, { 59 Ticket: "kythe://someCorpus#aTicketSig", 60 Fact: makeFactList("/kythe/node/kind", "testNode"), 61 }, { 62 Ticket: "kythe://someCorpus?lang=otpl#something", 63 Fact: makeFactList( 64 "/kythe/node/kind", "name", 65 "/some/other/fact", "value", 66 ), 67 }, { 68 Ticket: "kythe://someCorpus?lang=otpl#sig2", 69 Fact: makeFactList("/kythe/node/kind", "name"), 70 }, { 71 Ticket: "kythe://someCorpus?lang=otpl#sig3", 72 Fact: makeFactList("/kythe/node/kind", "name"), 73 }, { 74 Ticket: "kythe://someCorpus?lang=otpl#sig4", 75 Fact: makeFactList("/kythe/node/kind", "name"), 76 }, { 77 Ticket: "kythe://someCorpus?lang=otpl?path=/some/valid/path#a83md71", 78 Fact: makeFactList( 79 "/kythe/node/kind", "file", 80 "/kythe/text", "; some file content here\nfinal line\n", 81 "/kythe/text/encoding", "utf-8", 82 ), 83 }, { 84 Ticket: "kythe://c?lang=otpl?path=/a/path#6-9", 85 Fact: makeFactList( 86 "/kythe/node/kind", "anchor", 87 "/kythe/loc/start", "6", 88 "/kythe/loc/end", "9", 89 ), 90 }, { 91 Ticket: "kythe://c?lang=otpl?path=/a/path#27-33", 92 Fact: makeFactList( 93 "/kythe/node/kind", "anchor", 94 "/kythe/loc/start", "27", 95 "/kythe/loc/end", "33", 96 ), 97 }, { 98 Ticket: "kythe://c?lang=otpl?path=/a/path#map", 99 Fact: makeFactList("/kythe/node/kind", "function"), 100 }, { 101 Ticket: "kythe://core?lang=otpl#empty?", 102 Fact: makeFactList("/kythe/node/kind", "function"), 103 }, { 104 Ticket: "kythe://c?lang=otpl?path=/a/path#51-55", 105 Fact: makeFactList( 106 "/kythe/node/kind", "anchor", 107 "/kythe/loc/start", "51", 108 "/kythe/loc/end", "55", 109 ), 110 }, { 111 Ticket: "kythe://core?lang=otpl#cons", 112 Fact: makeFactList( 113 "/kythe/node/kind", "function", 114 // Canary to ensure we don't patch anchor facts in non-anchor nodes 115 "/kythe/loc/start", "51", 116 ), 117 }, { 118 Ticket: "kythe://c?path=/a/path", 119 Fact: makeFactList( 120 "/kythe/node/kind", "file", 121 "/kythe/text/encoding", "utf-8", 122 "/kythe/text", "some random text\nhere and \n there\nsome random text\nhere and \n there\n", 123 ), 124 }, { 125 Ticket: "kythe:?path=some/utf16/file", 126 Fact: []*cpb.Fact{{ 127 Name: "/kythe/text/encoding", 128 Value: []byte("utf-16le"), 129 }, { 130 Name: "/kythe/node/kind", 131 Value: []byte("file"), 132 }, { 133 Name: "/kythe/text", 134 Value: encodeText(utf16LE, "これはいくつかのテキストです\n"), 135 }}, 136 }, { 137 Ticket: "kythe:?path=some/utf16/file#0-4", 138 Fact: makeFactList( 139 "/kythe/node/kind", "anchor", 140 "/kythe/loc/start", "0", 141 "/kythe/loc/end", "4", 142 ), 143 }, { 144 Ticket: "kythe:#documented", 145 Fact: makeFactList( 146 "/kythe/node/kind", "record", 147 ), 148 DefinitionLocation: &srvpb.ExpandedAnchor{ 149 Ticket: "kythe:?path=def/location#defDoc", 150 Span: &cpb.Span{ 151 Start: &cpb.Point{ 152 ByteOffset: 1, 153 LineNumber: 1, 154 ColumnOffset: 1, 155 }, 156 End: &cpb.Point{ 157 ByteOffset: 4, 158 LineNumber: 1, 159 ColumnOffset: 4, 160 }, 161 }, 162 }, 163 }, { 164 Ticket: "kythe:#documentedBy", 165 Fact: makeFactList( 166 "/kythe/node/kind", "record", 167 ), 168 }, { 169 Ticket: "kythe:#childDoc", 170 Fact: makeFactList( 171 "/kythe/node/kind", "record", 172 ), 173 }, { 174 Ticket: "kythe:#childDocBy", 175 Fact: makeFactList( 176 "/kythe/node/kind", "record", 177 ), 178 }, { 179 Ticket: "kythe:#secondChildDoc", 180 Fact: makeFactList( 181 "/kythe/node/kind", "record", 182 ), 183 }, { 184 Ticket: "kythe://someCorpus?lang=otpl#withRelated", 185 Fact: makeFactList("/kythe/node/kind", "testNode"), 186 }, { 187 Ticket: "kythe://someCorpus?lang=otpl#withInfos", 188 Fact: makeFactList("/kythe/node/kind", "testNode"), 189 }, { 190 Ticket: "kythe:#aliasNode", 191 Fact: makeFactList("/kythe/node/kind", "talias"), 192 }, { 193 Ticket: "kythe:#indirect", 194 Fact: makeFactList("/kythe/node/kind", "indirect"), 195 }, 196 } 197 198 tbl = &testTable{ 199 Nodes: nodes, 200 Decorations: []*srvpb.FileDecorations{ 201 { 202 File: &srvpb.File{ 203 Ticket: "kythe://someCorpus?lang=otpl?path=/some/valid/path#a83md71", 204 Text: []byte("; some file content here\nfinal line\n"), 205 Encoding: "utf-8", 206 }, 207 }, 208 { 209 File: &srvpb.File{ 210 Ticket: "kythe://someCorpus?lang=otpl?path=/a/path#b7te37tn4", 211 Text: []byte(`(defn map [f coll] 212 (if (empty? coll) 213 [] 214 (cons (f (first coll)) (map f (rest coll))))) 215 `), 216 Encoding: "utf-8", 217 }, 218 Decoration: []*srvpb.FileDecorations_Decoration{ 219 { 220 Anchor: &srvpb.RawAnchor{ 221 Ticket: "kythe://c?lang=otpl?path=/a/path#6-9", 222 StartOffset: 6, 223 EndOffset: 9, 224 225 BuildConfiguration: "test-build-config", 226 }, 227 Kind: "/kythe/edge/defines/binding", 228 Target: "kythe://c?lang=otpl?path=/a/path#map", 229 }, 230 { 231 Anchor: &srvpb.RawAnchor{ 232 Ticket: "kythe://c?lang=otpl?path=/a/path#27-33", 233 StartOffset: 27, 234 EndOffset: 33, 235 }, 236 Kind: "/kythe/refs", 237 Target: "kythe://core?lang=otpl#empty?", 238 239 SemanticScope: "kythe://c?lang=otpl?path=/a/path#map", 240 }, 241 { 242 Anchor: &srvpb.RawAnchor{ 243 Ticket: "kythe://c?lang=otpl?path=/a/path#51-55", 244 StartOffset: 51, 245 EndOffset: 55, 246 }, 247 Kind: "/kythe/refs", 248 Target: "kythe://core?lang=otpl#cons", 249 250 SemanticScope: "kythe://c?lang=otpl?path=/a/path#map", 251 }, 252 }, 253 TargetOverride: []*srvpb.FileDecorations_Override{{ 254 Kind: srvpb.FileDecorations_Override_EXTENDS, 255 Overriding: "kythe://c?lang=otpl?path=/a/path#map", 256 Overridden: "kythe://c?lang=otpl#map", 257 OverriddenDefinition: "kythe://c?lang=otpl?path=/b/path#mapDef", 258 MarkedSource: &cpb.MarkedSource{ 259 Kind: cpb.MarkedSource_IDENTIFIER, 260 PreText: "OverrideMS", 261 }, 262 }, { 263 Kind: srvpb.FileDecorations_Override_EXTENDS, 264 Overriding: "kythe://c?lang=otpl?path=/a/path#map", 265 Overridden: "kythe://c?lang=otpl#map", 266 OverriddenDefinition: "kythe://c?lang=otpl?path=/b/path#mapDefOtherConfig", 267 MarkedSource: &cpb.MarkedSource{ 268 Kind: cpb.MarkedSource_IDENTIFIER, 269 PreText: "OverrideMS", 270 }, 271 }}, 272 Target: getNodes("kythe://c?lang=otpl?path=/a/path#map", "kythe://core?lang=otpl#empty?", "kythe://core?lang=otpl#cons"), 273 TargetDefinitions: []*srvpb.ExpandedAnchor{{ 274 Ticket: "kythe://c?lang=otpl?path=/b/path#mapDef", 275 BuildConfiguration: "test-build-config", 276 Span: &cpb.Span{ 277 Start: &cpb.Point{LineNumber: 1}, 278 End: &cpb.Point{ByteOffset: 4, LineNumber: 1, ColumnOffset: 4}, 279 }, 280 }, { 281 Ticket: "kythe://c?lang=otpl?path=/b/path#mapDefOtherConfig", 282 BuildConfiguration: "other-build-config", 283 Span: &cpb.Span{ 284 Start: &cpb.Point{LineNumber: 1}, 285 End: &cpb.Point{ByteOffset: 4, LineNumber: 1, ColumnOffset: 4}, 286 }, 287 }}, 288 Diagnostic: []*cpb.Diagnostic{ 289 {Message: "Test diagnostic message"}, 290 { 291 Span: &cpb.Span{ 292 Start: &cpb.Point{ 293 ByteOffset: 6, 294 LineNumber: 1, 295 ColumnOffset: 6, 296 }, 297 End: &cpb.Point{ 298 ByteOffset: 9, 299 LineNumber: 1, 300 ColumnOffset: 9, 301 }, 302 }, 303 Message: "Test diagnostic message w/ Span", 304 }, 305 }, 306 }, 307 { 308 File: &srvpb.File{ 309 Ticket: "kythe://corpus?path=/some/proto.proto?root=generated", 310 Text: []byte("// Generated by /some/proto.proto"), 311 Encoding: "utf-8", 312 }, 313 GeneratedBy: []string{"kythe://corpus?path=/some/proto.proto"}, 314 }, 315 { 316 File: &srvpb.File{ 317 Ticket: "kythe://corpus?path=file/infos", 318 Text: []byte(`some file with infos`), 319 Encoding: "utf-8", 320 }, 321 Decoration: []*srvpb.FileDecorations_Decoration{ 322 { 323 Anchor: &srvpb.RawAnchor{ 324 Ticket: "kythe://corpus?lang=lang?path=file/infos#0-4", 325 StartOffset: 0, 326 EndOffset: 4, 327 }, 328 Kind: "/kythe/edge/includes/ref", 329 Target: "kythe://corpus?path=another/file", 330 }, 331 { 332 Anchor: &srvpb.RawAnchor{ 333 Ticket: "kythe://corpus?lang=lang?path=file/infos#5-9", 334 StartOffset: 5, 335 EndOffset: 9, 336 }, 337 Kind: "/kythe/edge/ref", 338 Target: "kythe://corpus?path=def/file#node", 339 TargetDefinition: "kythe://corpus?path=def/file#anchor", 340 }, 341 }, 342 TargetDefinitions: []*srvpb.ExpandedAnchor{{ 343 Ticket: "kythe://corpus?path=def/file#anchor", 344 Span: &cpb.Span{ 345 Start: &cpb.Point{LineNumber: 1}, 346 End: &cpb.Point{ByteOffset: 4, LineNumber: 1, ColumnOffset: 4}, 347 }, 348 }}, 349 GeneratedBy: []string{"kythe://corpus?path=some/proto.proto"}, 350 FileInfo: []*srvpb.FileInfo{ 351 fi(cp("corpus", "", "file/infos"), "overallFileRev"), 352 fi(cp("corpus", "", "some/proto.proto"), "generatedRev"), 353 fi(cp("corpus", "", "another/file"), "anotherFileRev"), 354 fi(cp("corpus", "", "def/file"), "defFileRev"), 355 }, 356 }, 357 }, 358 359 RefSets: []*srvpb.PagedCrossReferences{{ 360 SourceTicket: "kythe://someCorpus?lang=otpl#signature", 361 SourceNode: getNode("kythe://someCorpus?lang=otpl#signature"), 362 363 Group: []*srvpb.PagedCrossReferences_Group{{ 364 BuildConfig: "testConfig", 365 Kind: "%/kythe/edge/defines/binding", 366 ScopedReference: []*srvpb.PagedCrossReferences_ScopedReference{{ 367 SemanticScope: "kythe://someCorpus?lang=otpl#scope", 368 Scope: &srvpb.ExpandedAnchor{ 369 Ticket: "kythe://c?lang=otpl?path=/a/path#scope", 370 BuildConfiguration: "testConfig", 371 Kind: "/kythe/edge/defines/binding", 372 373 Span: &cpb.Span{ 374 Start: &cpb.Point{ 375 ByteOffset: 27, 376 LineNumber: 2, 377 ColumnOffset: 10, 378 }, 379 End: &cpb.Point{ 380 ByteOffset: 33, 381 LineNumber: 3, 382 ColumnOffset: 5, 383 }, 384 }, 385 386 SnippetSpan: &cpb.Span{ 387 Start: &cpb.Point{ 388 ByteOffset: 17, 389 LineNumber: 2, 390 }, 391 End: &cpb.Point{ 392 ByteOffset: 27, 393 LineNumber: 2, 394 ColumnOffset: 10, 395 }, 396 }, 397 Snippet: "here and ", 398 }, 399 MarkedSource: &cpb.MarkedSource{ 400 Kind: cpb.MarkedSource_IDENTIFIER, 401 PreText: "Scope", 402 }, 403 Reference: []*srvpb.ExpandedAnchor{{ 404 Ticket: "kythe://c?lang=otpl?path=/a/path#27-33", 405 BuildConfiguration: "testConfig", 406 407 Span: &cpb.Span{ 408 Start: &cpb.Point{ 409 ByteOffset: 27, 410 LineNumber: 2, 411 ColumnOffset: 10, 412 }, 413 End: &cpb.Point{ 414 ByteOffset: 33, 415 LineNumber: 3, 416 ColumnOffset: 5, 417 }, 418 }, 419 420 SnippetSpan: &cpb.Span{ 421 Start: &cpb.Point{ 422 ByteOffset: 17, 423 LineNumber: 2, 424 }, 425 End: &cpb.Point{ 426 ByteOffset: 27, 427 LineNumber: 2, 428 ColumnOffset: 10, 429 }, 430 }, 431 Snippet: "here and ", 432 }}, 433 }}, 434 }}, 435 PageIndex: []*srvpb.PagedCrossReferences_PageIndex{{ 436 PageKey: "aBcDeFg", 437 Kind: "%/kythe/edge/ref", 438 Count: 2, 439 }}, 440 }, { 441 SourceTicket: "kythe://someCorpus?lang=otpl#withRelated", 442 SourceNode: getNode("kythe://someCorpus?lang=otpl#withRelated"), 443 MarkedSource: &cpb.MarkedSource{ 444 Kind: cpb.MarkedSource_IDENTIFIER, 445 PreText: "id", 446 }, 447 448 Group: []*srvpb.PagedCrossReferences_Group{{ 449 Kind: "/kythe/edge/extends", 450 RelatedNode: []*srvpb.PagedCrossReferences_RelatedNode{{ 451 Node: &srvpb.Node{ 452 Ticket: "kythe:#someRelatedNode", 453 }, 454 }}, 455 }, { 456 Kind: "/kythe/edge/param", 457 RelatedNode: []*srvpb.PagedCrossReferences_RelatedNode{{ 458 Ordinal: 0, 459 Node: &srvpb.Node{ 460 Ticket: "kythe:#someParameter0", 461 }, 462 }, { 463 Ordinal: 1, 464 Node: &srvpb.Node{ 465 Ticket: "kythe:#someParameter1", 466 }, 467 }}, 468 }}, 469 }, { 470 SourceTicket: "kythe://someCorpus?lang=otpl#withInfos", 471 SourceNode: getNode("kythe://someCorpus?lang=otpl#withInfos"), 472 473 Group: []*srvpb.PagedCrossReferences_Group{{ 474 Kind: "%/kythe/edge/ref", 475 Anchor: []*srvpb.ExpandedAnchor{{ 476 Ticket: "kythe://corpus?lang=otpl?path=some/file#27-33", 477 478 Span: &cpb.Span{ 479 Start: &cpb.Point{ 480 ByteOffset: 27, 481 LineNumber: 2, 482 ColumnOffset: 10, 483 }, 484 End: &cpb.Point{ 485 ByteOffset: 33, 486 LineNumber: 3, 487 ColumnOffset: 5, 488 }, 489 }, 490 }}, 491 492 FileInfo: []*srvpb.FileInfo{ 493 fi(cp("corpus", "", "some/file"), "someFileRev"), 494 }, 495 }}, 496 }, { 497 SourceTicket: "kythe://someCorpus?lang=otpl#withMerge", 498 SourceNode: getNode("kythe://someCorpus?lang=otpl#withMerge"), 499 MergeWith: []string{ 500 "kythe://someCorpus?lang=otpl#withCallers", 501 "kythe://someCorpus?lang=otpl#withRelated", 502 }, 503 }, { 504 SourceTicket: "kythe://someCorpus?lang=otpl#withCallers", 505 SourceNode: getNode("kythe://someCorpus?lang=otpl#withCallers"), 506 507 Group: []*srvpb.PagedCrossReferences_Group{{ 508 Kind: "#internal/ref/call/direct", 509 Caller: []*srvpb.PagedCrossReferences_Caller{{ 510 Caller: &srvpb.ExpandedAnchor{ 511 Ticket: "kythe:?path=someFile#someCallerAnchor", 512 Span: arbitrarySpan, 513 }, 514 MarkedSource: &cpb.MarkedSource{ 515 Kind: cpb.MarkedSource_IDENTIFIER, 516 PreText: "id", 517 }, 518 SemanticCaller: "kythe:#someCaller", 519 Callsite: []*srvpb.ExpandedAnchor{{ 520 Ticket: "kythe:?path=someFile#someCallsiteAnchor", 521 }}, 522 }}, 523 }, { 524 Kind: "#internal/ref/call/override", 525 Caller: []*srvpb.PagedCrossReferences_Caller{{ 526 Caller: &srvpb.ExpandedAnchor{ 527 Ticket: "kythe:?path=someFile#someOverrideCallerAnchor1", 528 Span: arbitrarySpan, 529 }, 530 Callsite: []*srvpb.ExpandedAnchor{{ 531 Ticket: "kythe:?path=someFile#someCallsiteAnchor", 532 Span: arbitrarySpan, 533 }}, 534 }, { 535 Caller: &srvpb.ExpandedAnchor{ 536 Ticket: "kythe:?path=someFile#someOverrideCallerAnchor2", 537 Span: arbitrarySpan, 538 }, 539 Callsite: []*srvpb.ExpandedAnchor{{ 540 Ticket: "kythe:?path=someFile#someCallsiteAnchor", 541 }}, 542 }}, 543 }}, 544 }, { 545 SourceTicket: "kythe:#aliasNode", 546 SourceNode: getNode("kythe:#aliasNode"), 547 Group: []*srvpb.PagedCrossReferences_Group{{ 548 Kind: "%/kythe/edge/aliases", 549 RelatedNode: []*srvpb.PagedCrossReferences_RelatedNode{{ 550 Node: getNode("kythe://someCorpus?lang=otpl#signature"), 551 }}, 552 }, { 553 Kind: "/kythe/edge/indirect", 554 RelatedNode: []*srvpb.PagedCrossReferences_RelatedNode{{ 555 Node: getNode("kythe:#indirect"), 556 }}, 557 }, { 558 Kind: "%/kythe/edge/ref", 559 Anchor: []*srvpb.ExpandedAnchor{{ 560 Ticket: "kythe:?path=somewhere#0-9", 561 562 Span: &cpb.Span{ 563 Start: &cpb.Point{LineNumber: 1}, 564 End: &cpb.Point{ByteOffset: 9, LineNumber: 1, ColumnOffset: 9}, 565 }, 566 }}, 567 }}, 568 }, { 569 SourceTicket: "kythe:#indirect", 570 SourceNode: getNode("kythe:#indirect"), 571 Group: []*srvpb.PagedCrossReferences_Group{{ 572 Kind: "%/kythe/edge/ref", 573 Anchor: []*srvpb.ExpandedAnchor{{ 574 Ticket: "kythe:?path=somewhereElse#0-9", 575 576 Span: &cpb.Span{ 577 Start: &cpb.Point{LineNumber: 1}, 578 End: &cpb.Point{ByteOffset: 9, LineNumber: 1, ColumnOffset: 9}, 579 }, 580 }}, 581 }}, 582 PageIndex: []*srvpb.PagedCrossReferences_PageIndex{{ 583 PageKey: "indirectPage", 584 Kind: "/kythe/edge/indirect", 585 Count: 1, 586 }}, 587 }}, 588 RefPages: []*srvpb.PagedCrossReferences_Page{{ 589 PageKey: "aBcDeFg", 590 Group: &srvpb.PagedCrossReferences_Group{ 591 Kind: "%/kythe/edge/ref", 592 Anchor: []*srvpb.ExpandedAnchor{{ 593 Ticket: "kythe:?path=some/utf16/file#0-4", 594 595 Span: &cpb.Span{ 596 Start: &cpb.Point{LineNumber: 1}, 597 End: &cpb.Point{ByteOffset: 4, LineNumber: 1, ColumnOffset: 4}, 598 }, 599 600 SnippetSpan: &cpb.Span{ 601 Start: &cpb.Point{ 602 LineNumber: 1, 603 }, 604 End: &cpb.Point{ 605 ByteOffset: 28, 606 LineNumber: 1, 607 ColumnOffset: 28, 608 }, 609 }, 610 Snippet: "これはいくつかのテキストです", 611 }, { 612 Ticket: "kythe://c?lang=otpl?path=/a/path#51-55", 613 614 Span: &cpb.Span{ 615 Start: &cpb.Point{ 616 ByteOffset: 51, 617 LineNumber: 4, 618 ColumnOffset: 15, 619 }, 620 End: &cpb.Point{ 621 ByteOffset: 55, 622 LineNumber: 5, 623 ColumnOffset: 2, 624 }, 625 }, 626 627 SnippetSpan: &cpb.Span{ 628 Start: &cpb.Point{ 629 ByteOffset: 36, 630 LineNumber: 4, 631 }, 632 End: &cpb.Point{ 633 ByteOffset: 52, 634 LineNumber: 4, 635 ColumnOffset: 16, 636 }, 637 }, 638 Snippet: "some random text", 639 }}, 640 }, 641 }, { 642 PageKey: "indirectPage", 643 Group: &srvpb.PagedCrossReferences_Group{ 644 Kind: "/kythe/edge/indirect", 645 RelatedNode: []*srvpb.PagedCrossReferences_RelatedNode{{ 646 Node: getNode("kythe://someCorpus?lang=otpl#signature"), 647 }}, 648 }, 649 }}, 650 651 Documents: []*srvpb.Document{{ 652 Ticket: "kythe:#documented", 653 RawText: "some documentation text", 654 MarkedSource: &cpb.MarkedSource{ 655 Kind: cpb.MarkedSource_IDENTIFIER, 656 PreText: "DocumentBuilderFactory", 657 }, 658 Node: getNodes("kythe:#documented"), 659 ChildTicket: []string{"kythe:#childDoc", "kythe:#childDocBy"}, 660 }, { 661 Ticket: "kythe:#documentedBy", 662 DocumentedBy: "kythe:#documented", 663 RawText: "replaced documentation text", 664 MarkedSource: &cpb.MarkedSource{ 665 Kind: cpb.MarkedSource_IDENTIFIER, 666 PreText: "ReplacedDocumentBuilderFactory", 667 }, 668 Node: getNodes("kythe:#documentedBy"), 669 }, { 670 Ticket: "kythe:#childDoc", 671 RawText: "child document text", 672 Node: getNodes("kythe:#childDoc"), 673 }, { 674 Ticket: "kythe:#childDocBy", 675 DocumentedBy: "kythe:#secondChildDoc", 676 Node: getNodes("kythe:#childDocBy"), 677 }, { 678 Ticket: "kythe:#secondChildDoc", 679 RawText: "second child document text", 680 Node: getNodes("kythe:#secondChildDoc"), 681 }}, 682 } 683 684 arbitrarySpan = &cpb.Span{ 685 Start: &cpb.Point{LineNumber: 1}, 686 End: &cpb.Point{ByteOffset: 4, LineNumber: 1, ColumnOffset: 4}, 687 } 688 ) 689 690 func getNodes(ts ...string) []*srvpb.Node { 691 var res []*srvpb.Node 692 for _, t := range ts { 693 res = append(res, getNode(t)) 694 } 695 return res 696 } 697 698 func getNode(t string) *srvpb.Node { 699 for _, n := range nodes { 700 if n.Ticket == t { 701 return n 702 } 703 } 704 return &srvpb.Node{Ticket: t} 705 } 706 707 var utf16LE = unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM) 708 709 func encodeText(e encoding.Encoding, text string) []byte { 710 res, _, err := transform.Bytes(e.NewEncoder(), []byte(text)) 711 if err != nil { 712 panic(err) 713 } 714 return res 715 } 716 717 func TestDecorationsRefs(t *testing.T) { 718 d := tbl.Decorations[1] 719 720 st := tbl.Construct(t) 721 reply, err := st.Decorations(ctx, &xpb.DecorationsRequest{ 722 Location: &xpb.Location{Ticket: d.File.Ticket}, 723 References: true, 724 Filter: []string{"**"}, 725 }) 726 testutil.Fatalf(t, "DecorationsRequest error: %v", err) 727 728 if reply.SourceText != nil { 729 t.Errorf("Unexpected source text: %q", string(reply.SourceText)) 730 } 731 if reply.Encoding != "" { 732 t.Errorf("Unexpected encoding: %q", reply.Encoding) 733 } 734 735 expected := refs(span.NewNormalizer(d.File.Text), d.Decoration, d.FileInfo) 736 for _, ref := range expected { 737 ref.SemanticScope = "" // not requested 738 } 739 if err := testutil.DeepEqual(expected, reply.Reference); err != nil { 740 t.Fatal(err) 741 } 742 743 expectedNodes := nodeInfos(tbl.Nodes[9:11], tbl.Nodes[12:13]) 744 if err := testutil.DeepEqual(expectedNodes, reply.Nodes); err != nil { 745 t.Fatal(err) 746 } 747 } 748 749 func TestDecorationsRefScopes(t *testing.T) { 750 d := tbl.Decorations[1] 751 752 st := tbl.Construct(t) 753 reply, err := st.Decorations(ctx, &xpb.DecorationsRequest{ 754 Location: &xpb.Location{Ticket: d.File.Ticket}, 755 References: true, 756 SemanticScopes: true, 757 }) 758 testutil.Fatalf(t, "DecorationsRequest error: %v", err) 759 760 expected := refs(span.NewNormalizer(d.File.Text), d.Decoration, d.FileInfo) 761 if err := testutil.DeepEqual(expected, reply.Reference); err != nil { 762 t.Fatal(err) 763 } 764 } 765 766 func TestDecorationsExtendsOverrides(t *testing.T) { 767 d := tbl.Decorations[1] 768 769 st := tbl.Construct(t) 770 reply, err := st.Decorations(ctx, &xpb.DecorationsRequest{ 771 Location: &xpb.Location{Ticket: d.File.Ticket}, 772 References: true, 773 ExtendsOverrides: true, 774 SemanticScopes: true, 775 TargetDefinitions: true, 776 }) 777 testutil.Fatalf(t, "DecorationsRequest error: %v", err) 778 779 expectedOverrides := map[string]*xpb.DecorationsReply_Overrides{ 780 "kythe://c?lang=otpl?path=/a/path#map": &xpb.DecorationsReply_Overrides{ 781 Override: []*xpb.DecorationsReply_Override{{ 782 Kind: xpb.DecorationsReply_Override_EXTENDS, 783 Target: "kythe://c?lang=otpl#map", 784 TargetDefinition: "kythe://c?lang=otpl?path=/b/path#mapDef", 785 MarkedSource: &cpb.MarkedSource{ 786 Kind: cpb.MarkedSource_IDENTIFIER, 787 PreText: "OverrideMS", 788 }, 789 }, { 790 Kind: xpb.DecorationsReply_Override_EXTENDS, 791 Target: "kythe://c?lang=otpl#map", 792 TargetDefinition: "kythe://c?lang=otpl?path=/b/path#mapDefOtherConfig", 793 MarkedSource: &cpb.MarkedSource{ 794 Kind: cpb.MarkedSource_IDENTIFIER, 795 PreText: "OverrideMS", 796 }, 797 }}, 798 }, 799 } 800 if err := testutil.DeepEqual(expectedOverrides, reply.ExtendsOverrides); err != nil { 801 t.Fatal(err) 802 } 803 804 expectedDefs := map[string]*xpb.Anchor{ 805 "kythe://c?lang=otpl?path=/b/path#mapDef": &xpb.Anchor{ 806 Ticket: "kythe://c?lang=otpl?path=/b/path#mapDef", 807 Parent: "kythe://c?path=/b/path", 808 BuildConfig: "test-build-config", 809 Span: &cpb.Span{ 810 Start: &cpb.Point{LineNumber: 1}, 811 End: &cpb.Point{ByteOffset: 4, LineNumber: 1, ColumnOffset: 4}, 812 }, 813 }, 814 "kythe://c?lang=otpl?path=/b/path#mapDefOtherConfig": &xpb.Anchor{ 815 Ticket: "kythe://c?lang=otpl?path=/b/path#mapDefOtherConfig", 816 Parent: "kythe://c?path=/b/path", 817 BuildConfig: "other-build-config", 818 Span: &cpb.Span{ 819 Start: &cpb.Point{LineNumber: 1}, 820 End: &cpb.Point{ByteOffset: 4, LineNumber: 1, ColumnOffset: 4}, 821 }, 822 }, 823 } 824 if err := testutil.DeepEqual(expectedDefs, reply.DefinitionLocations); err != nil { 825 t.Fatal(err) 826 } 827 } 828 829 func TestDecorationsBuildConfig(t *testing.T) { 830 d := tbl.Decorations[1] 831 st := tbl.Construct(t) 832 833 t.Run("MissingConfig", func(t *testing.T) { 834 reply, err := st.Decorations(ctx, &xpb.DecorationsRequest{ 835 Location: &xpb.Location{Ticket: d.File.Ticket}, 836 References: true, 837 BuildConfig: []string{"missing-build-config"}, 838 ExtendsOverrides: true, 839 TargetDefinitions: true, 840 }) 841 testutil.Fatalf(t, "DecorationsRequest error: %v", err) 842 843 if err := testutil.DeepEqual([]*xpb.DecorationsReply_Reference{}, reply.Reference); err != nil { 844 t.Fatal(err) 845 } 846 }) 847 848 t.Run("FoundConfig", func(t *testing.T) { 849 reply, err := st.Decorations(ctx, &xpb.DecorationsRequest{ 850 Location: &xpb.Location{Ticket: d.File.Ticket}, 851 References: true, 852 BuildConfig: []string{"test-build-config"}, 853 ExtendsOverrides: true, 854 TargetDefinitions: true, 855 }) 856 testutil.Fatalf(t, "DecorationsRequest error: %v", err) 857 858 expected := refs(span.NewNormalizer(d.File.Text), d.Decoration[:1], d.FileInfo) 859 if err := testutil.DeepEqual(expected, reply.Reference); err != nil { 860 t.Fatal(err) 861 } 862 863 expectedOverrides := map[string]*xpb.DecorationsReply_Overrides{ 864 "kythe://c?lang=otpl?path=/a/path#map": &xpb.DecorationsReply_Overrides{ 865 Override: []*xpb.DecorationsReply_Override{{ 866 Kind: xpb.DecorationsReply_Override_EXTENDS, 867 Target: "kythe://c?lang=otpl#map", 868 TargetDefinition: "kythe://c?lang=otpl?path=/b/path#mapDef", 869 MarkedSource: &cpb.MarkedSource{ 870 Kind: cpb.MarkedSource_IDENTIFIER, 871 PreText: "OverrideMS", 872 }, 873 }}, 874 }, 875 } 876 if err := testutil.DeepEqual(expectedOverrides, reply.ExtendsOverrides); err != nil { 877 t.Fatal(err) 878 } 879 880 expectedDefs := map[string]*xpb.Anchor{ 881 "kythe://c?lang=otpl?path=/b/path#mapDef": &xpb.Anchor{ 882 Ticket: "kythe://c?lang=otpl?path=/b/path#mapDef", 883 Parent: "kythe://c?path=/b/path", 884 BuildConfig: "test-build-config", 885 Span: &cpb.Span{ 886 Start: &cpb.Point{LineNumber: 1}, 887 End: &cpb.Point{ByteOffset: 4, LineNumber: 1, ColumnOffset: 4}, 888 }, 889 }, 890 } 891 if err := testutil.DeepEqual(expectedDefs, reply.DefinitionLocations); err != nil { 892 t.Fatal(err) 893 } 894 }) 895 } 896 897 func TestDecorationsDirtyBuffer(t *testing.T) { 898 d := tbl.Decorations[1] 899 900 st := tbl.Construct(t) 901 // s/empty?/seq/ 902 dirty := []byte(`(defn map [f coll] 903 (if (seq coll) 904 [] 905 (cons (f (first coll)) (map f (rest coll))))) 906 `) 907 reply, err := st.Decorations(ctx, &xpb.DecorationsRequest{ 908 Location: &xpb.Location{Ticket: d.File.Ticket}, 909 DirtyBuffer: dirty, 910 References: true, 911 Filter: []string{"**"}, 912 }) 913 testutil.Fatalf(t, "DecorationsRequest error: %v", err) 914 915 if reply.SourceText != nil { 916 t.Errorf("Unexpected source text: %q", string(reply.SourceText)) 917 } 918 if reply.Encoding != "" { 919 t.Errorf("Unexpected encoding: %q", reply.Encoding) 920 } 921 922 expected := []*xpb.DecorationsReply_Reference{ 923 { 924 // Unpatched anchor for "map" 925 TargetTicket: "kythe://c?lang=otpl?path=/a/path#map", 926 Kind: "/kythe/edge/defines/binding", 927 928 Span: &cpb.Span{ 929 Start: &cpb.Point{ 930 ByteOffset: 6, 931 LineNumber: 1, 932 ColumnOffset: 6, 933 }, 934 End: &cpb.Point{ 935 ByteOffset: 9, 936 LineNumber: 1, 937 ColumnOffset: 9, 938 }, 939 }, 940 941 BuildConfig: "test-build-config", 942 }, 943 // Skipped anchor for "empty?" (inside edit region) 944 { 945 // Patched anchor for "cons" (moved backwards by 3 bytes) 946 TargetTicket: "kythe://core?lang=otpl#cons", 947 Kind: "/kythe/refs", 948 Span: &cpb.Span{ 949 Start: &cpb.Point{ 950 ByteOffset: 48, 951 LineNumber: 4, 952 ColumnOffset: 5, 953 }, 954 End: &cpb.Point{ 955 ByteOffset: 52, 956 LineNumber: 4, 957 ColumnOffset: 9, 958 }, 959 }, 960 }, 961 } 962 if err := testutil.DeepEqual(expected, reply.Reference); err != nil { 963 t.Fatal(err) 964 } 965 966 // These are a subset of the anchor nodes in tbl.Decorations[1]. tbl.Nodes[10] is missing because 967 // it is the target of an anchor in the edited region. 968 expectedNodes := nodeInfos([]*srvpb.Node{tbl.Nodes[9], tbl.Nodes[12]}) 969 if err := testutil.DeepEqual(expectedNodes, reply.Nodes); err != nil { 970 t.Fatal(err) 971 } 972 } 973 974 func TestDecorationsNotFound(t *testing.T) { 975 st := tbl.Construct(t) 976 reply, err := st.Decorations(ctx, &xpb.DecorationsRequest{ 977 Location: &xpb.Location{ 978 Ticket: "kythe:#someMissingFileTicket", 979 }, 980 }) 981 982 if err == nil { 983 t.Fatalf("Unexpected DecorationsReply: {%v}", reply) 984 } else if err != xrefs.ErrDecorationsNotFound { 985 t.Fatalf("Unexpected Decorations error: %v", err) 986 } 987 } 988 989 func TestDecorationsEmpty(t *testing.T) { 990 st := tbl.Construct(t) 991 reply, err := st.Decorations(ctx, &xpb.DecorationsRequest{ 992 Location: &xpb.Location{ 993 Ticket: tbl.Decorations[0].File.Ticket, 994 }, 995 References: true, 996 }) 997 testutil.Fatalf(t, "DecorationsRequest error: %v", err) 998 999 if len(reply.Reference) > 0 { 1000 t.Fatalf("Unexpected DecorationsReply: {%v}", reply) 1001 } 1002 } 1003 1004 func TestDecorationsSourceText(t *testing.T) { 1005 expected := tbl.Decorations[0] 1006 1007 st := tbl.Construct(t) 1008 reply, err := st.Decorations(ctx, &xpb.DecorationsRequest{ 1009 Location: &xpb.Location{Ticket: expected.File.Ticket}, 1010 SourceText: true, 1011 }) 1012 testutil.Fatalf(t, "DecorationsRequest error: %v", err) 1013 1014 if !bytes.Equal(reply.SourceText, expected.File.Text) { 1015 t.Errorf("Expected source text %q; found %q", string(expected.File.Text), string(reply.SourceText)) 1016 } 1017 if reply.Encoding != expected.File.Encoding { 1018 t.Errorf("Expected source text %q; found %q", expected.File.Encoding, reply.Encoding) 1019 } 1020 if len(reply.Reference) > 0 { 1021 t.Errorf("Unexpected references in DecorationsReply %v", reply.Reference) 1022 } 1023 } 1024 1025 func TestDecorationsGeneratedBy(t *testing.T) { 1026 st := tbl.Construct(t) 1027 reply, err := st.Decorations(ctx, &xpb.DecorationsRequest{ 1028 Location: &xpb.Location{Ticket: "kythe://corpus?path=/some/proto.proto?root=generated"}, 1029 }) 1030 testutil.Fatalf(t, "DecorationsRequest error: %v", err) 1031 1032 expected := &xpb.DecorationsReply{ 1033 Location: &xpb.Location{Ticket: "kythe://corpus?path=/some/proto.proto?root=generated"}, 1034 GeneratedByFile: []*xpb.File{{ 1035 CorpusPath: &cpb.CorpusPath{ 1036 Corpus: "corpus", 1037 Path: "/some/proto.proto", 1038 }, 1039 }}, 1040 } 1041 1042 if diff := compare.ProtoDiff(expected, reply); diff != "" { 1043 t.Fatalf("Unexpected diff (- expected; + found):\n%s", diff) 1044 } 1045 } 1046 1047 func TestDecorationsRevisions(t *testing.T) { 1048 st := tbl.Construct(t) 1049 1050 t.Run("file/generated_by", func(t *testing.T) { 1051 reply, err := st.Decorations(ctx, &xpb.DecorationsRequest{ 1052 Location: &xpb.Location{Ticket: "kythe://corpus?path=file/infos"}, 1053 }) 1054 testutil.Fatalf(t, "DecorationsRequest error: %v", err) 1055 1056 expected := &xpb.DecorationsReply{ 1057 Location: &xpb.Location{Ticket: "kythe://corpus?path=file/infos"}, 1058 Revision: "overallFileRev", 1059 GeneratedByFile: []*xpb.File{{ 1060 CorpusPath: &cpb.CorpusPath{ 1061 Corpus: "corpus", 1062 Path: "some/proto.proto", 1063 }, 1064 Revision: "generatedRev", 1065 }}, 1066 } 1067 1068 if diff := compare.ProtoDiff(expected, reply); diff != "" { 1069 t.Fatalf("Unexpected diff (- expected; + found):\n%s", diff) 1070 } 1071 }) 1072 1073 t.Run("target_revision", func(t *testing.T) { 1074 reply, err := st.Decorations(ctx, &xpb.DecorationsRequest{ 1075 Location: &xpb.Location{Ticket: "kythe://corpus?path=file/infos"}, 1076 References: true, 1077 }) 1078 testutil.Fatalf(t, "DecorationsRequest error: %v", err) 1079 1080 expected := &xpb.DecorationsReply{ 1081 Location: &xpb.Location{Ticket: "kythe://corpus?path=file/infos"}, 1082 Revision: "overallFileRev", 1083 GeneratedByFile: []*xpb.File{{ 1084 CorpusPath: &cpb.CorpusPath{ 1085 Corpus: "corpus", 1086 Path: "some/proto.proto", 1087 }, 1088 Revision: "generatedRev", 1089 }}, 1090 Reference: []*xpb.DecorationsReply_Reference{{ 1091 TargetTicket: "kythe://corpus?path=another/file", 1092 Kind: "/kythe/edge/includes/ref", 1093 TargetRevision: "anotherFileRev", 1094 Span: &cpb.Span{ 1095 Start: &cpb.Point{ 1096 ByteOffset: 0, 1097 LineNumber: 1, 1098 ColumnOffset: 0, 1099 }, 1100 End: &cpb.Point{ 1101 ByteOffset: 4, 1102 LineNumber: 1, 1103 ColumnOffset: 4, 1104 }, 1105 }, 1106 }, { 1107 TargetTicket: "kythe://corpus?path=def/file#node", 1108 Kind: "/kythe/edge/ref", 1109 Span: &cpb.Span{ 1110 Start: &cpb.Point{ 1111 ByteOffset: 5, 1112 LineNumber: 1, 1113 ColumnOffset: 5, 1114 }, 1115 End: &cpb.Point{ 1116 ByteOffset: 9, 1117 LineNumber: 1, 1118 ColumnOffset: 9, 1119 }, 1120 }, 1121 }}, 1122 } 1123 1124 if diff := compare.ProtoDiff(expected, reply); diff != "" { 1125 t.Fatalf("Unexpected diff (- expected; + found):\n%s", diff) 1126 } 1127 }) 1128 1129 t.Run("target_defs", func(t *testing.T) { 1130 reply, err := st.Decorations(ctx, &xpb.DecorationsRequest{ 1131 Location: &xpb.Location{Ticket: "kythe://corpus?path=file/infos"}, 1132 References: true, 1133 TargetDefinitions: true, 1134 }) 1135 testutil.Fatalf(t, "DecorationsRequest error: %v", err) 1136 1137 expected := &xpb.DecorationsReply{ 1138 Location: &xpb.Location{Ticket: "kythe://corpus?path=file/infos"}, 1139 Revision: "overallFileRev", 1140 GeneratedByFile: []*xpb.File{{ 1141 CorpusPath: &cpb.CorpusPath{ 1142 Corpus: "corpus", 1143 Path: "some/proto.proto", 1144 }, 1145 Revision: "generatedRev", 1146 }}, 1147 Reference: []*xpb.DecorationsReply_Reference{{ 1148 TargetTicket: "kythe://corpus?path=another/file", 1149 Kind: "/kythe/edge/includes/ref", 1150 TargetRevision: "anotherFileRev", 1151 Span: &cpb.Span{ 1152 Start: &cpb.Point{ 1153 ByteOffset: 0, 1154 LineNumber: 1, 1155 ColumnOffset: 0, 1156 }, 1157 End: &cpb.Point{ 1158 ByteOffset: 4, 1159 LineNumber: 1, 1160 ColumnOffset: 4, 1161 }, 1162 }, 1163 }, { 1164 TargetTicket: "kythe://corpus?path=def/file#node", 1165 TargetDefinition: "kythe://corpus?path=def/file#anchor", 1166 Kind: "/kythe/edge/ref", 1167 Span: &cpb.Span{ 1168 Start: &cpb.Point{ 1169 ByteOffset: 5, 1170 LineNumber: 1, 1171 ColumnOffset: 5, 1172 }, 1173 End: &cpb.Point{ 1174 ByteOffset: 9, 1175 LineNumber: 1, 1176 ColumnOffset: 9, 1177 }, 1178 }, 1179 }}, 1180 DefinitionLocations: map[string]*xpb.Anchor{ 1181 "kythe://corpus?path=def/file#anchor": &xpb.Anchor{ 1182 Ticket: "kythe://corpus?path=def/file#anchor", 1183 Parent: "kythe://corpus?path=def/file", 1184 Revision: "defFileRev", 1185 Span: &cpb.Span{ 1186 Start: &cpb.Point{LineNumber: 1}, 1187 End: &cpb.Point{ByteOffset: 4, LineNumber: 1, ColumnOffset: 4}, 1188 }, 1189 }, 1190 }, 1191 } 1192 1193 if diff := compare.ProtoDiff(expected, reply); diff != "" { 1194 t.Fatalf("Unexpected diff (- expected; + found):\n%s", diff) 1195 } 1196 }) 1197 } 1198 1199 func TestDecorationsDiagnostics(t *testing.T) { 1200 d := tbl.Decorations[1] 1201 1202 st := tbl.Construct(t) 1203 reply, err := st.Decorations(ctx, &xpb.DecorationsRequest{ 1204 Location: &xpb.Location{Ticket: d.File.Ticket}, 1205 Diagnostics: true, 1206 }) 1207 testutil.Fatalf(t, "DecorationsRequest error: %v", err) 1208 1209 expected := tbl.Decorations[1].Diagnostic 1210 if err := testutil.DeepEqual(expected, reply.Diagnostic); err != nil { 1211 t.Fatal(err) 1212 } 1213 } 1214 1215 func TestCrossReferencesNone(t *testing.T) { 1216 st := tbl.Construct(t) 1217 reply, err := st.CrossReferences(ctx, &xpb.CrossReferencesRequest{ 1218 Ticket: []string{"kythe://someCorpus?lang=otpl#sig2"}, 1219 DefinitionKind: xpb.CrossReferencesRequest_ALL_DEFINITIONS, 1220 ReferenceKind: xpb.CrossReferencesRequest_ALL_REFERENCES, 1221 }) 1222 testutil.Fatalf(t, "CrossReferencesRequest error: %v", err) 1223 1224 if len(reply.CrossReferences) > 0 || len(reply.Nodes) > 0 { 1225 t.Fatalf("Expected empty CrossReferencesReply; found %v", reply) 1226 } 1227 } 1228 1229 func TestCrossReferences(t *testing.T) { 1230 ticket := "kythe://someCorpus?lang=otpl#signature" 1231 1232 st := tbl.Construct(t) 1233 reply, err := st.CrossReferences(ctx, &xpb.CrossReferencesRequest{ 1234 Ticket: []string{ticket}, 1235 DefinitionKind: xpb.CrossReferencesRequest_BINDING_DEFINITIONS, 1236 ReferenceKind: xpb.CrossReferencesRequest_ALL_REFERENCES, 1237 Snippets: xpb.SnippetsKind_DEFAULT, 1238 }) 1239 testutil.Fatalf(t, "CrossReferencesRequest error: %v", err) 1240 1241 expected := &xpb.CrossReferencesReply_CrossReferenceSet{ 1242 Ticket: ticket, 1243 1244 Reference: []*xpb.CrossReferencesReply_RelatedAnchor{{Anchor: &xpb.Anchor{ 1245 Ticket: "kythe:?path=some/utf16/file#0-4", 1246 Kind: "/kythe/edge/ref", 1247 Parent: "kythe:?path=some/utf16/file", 1248 1249 Span: &cpb.Span{ 1250 Start: &cpb.Point{LineNumber: 1}, 1251 End: &cpb.Point{ByteOffset: 4, LineNumber: 1, ColumnOffset: 4}, 1252 }, 1253 1254 SnippetSpan: &cpb.Span{ 1255 Start: &cpb.Point{ 1256 LineNumber: 1, 1257 }, 1258 End: &cpb.Point{ 1259 ByteOffset: 28, 1260 LineNumber: 1, 1261 ColumnOffset: 28, 1262 }, 1263 }, 1264 Snippet: "これはいくつかのテキストです", 1265 }}, {Anchor: &xpb.Anchor{ 1266 Ticket: "kythe://c?lang=otpl?path=/a/path#51-55", 1267 Kind: "/kythe/edge/ref", 1268 Parent: "kythe://c?path=/a/path", 1269 1270 Span: &cpb.Span{ 1271 Start: &cpb.Point{ 1272 ByteOffset: 51, 1273 LineNumber: 4, 1274 ColumnOffset: 15, 1275 }, 1276 End: &cpb.Point{ 1277 ByteOffset: 55, 1278 LineNumber: 5, 1279 ColumnOffset: 2, 1280 }, 1281 }, 1282 1283 SnippetSpan: &cpb.Span{ 1284 Start: &cpb.Point{ 1285 ByteOffset: 36, 1286 LineNumber: 4, 1287 }, 1288 End: &cpb.Point{ 1289 ByteOffset: 52, 1290 LineNumber: 4, 1291 ColumnOffset: 16, 1292 }, 1293 }, 1294 Snippet: "some random text", 1295 }}}, 1296 1297 Definition: []*xpb.CrossReferencesReply_RelatedAnchor{{Anchor: &xpb.Anchor{ 1298 Ticket: "kythe://c?lang=otpl?path=/a/path#27-33", 1299 Kind: "/kythe/edge/defines/binding", 1300 Parent: "kythe://c?path=/a/path", 1301 BuildConfig: "testConfig", 1302 1303 Span: &cpb.Span{ 1304 Start: &cpb.Point{ 1305 ByteOffset: 27, 1306 LineNumber: 2, 1307 ColumnOffset: 10, 1308 }, 1309 End: &cpb.Point{ 1310 ByteOffset: 33, 1311 LineNumber: 3, 1312 ColumnOffset: 5, 1313 }, 1314 }, 1315 1316 SnippetSpan: &cpb.Span{ 1317 Start: &cpb.Point{ 1318 ByteOffset: 17, 1319 LineNumber: 2, 1320 }, 1321 End: &cpb.Point{ 1322 ByteOffset: 27, 1323 LineNumber: 2, 1324 ColumnOffset: 10, 1325 }, 1326 }, 1327 Snippet: "here and ", 1328 }}}, 1329 } 1330 1331 if err := testutil.DeepEqual(&xpb.CrossReferencesReply_Total{ 1332 Definitions: 1, 1333 References: 2, 1334 RefEdgeToCount: map[string]int64{ 1335 "/kythe/edge/ref": 2, 1336 }, 1337 }, reply.Total); err != nil { 1338 t.Error(err) 1339 } 1340 wantFiltered := &xpb.CrossReferencesReply_Total{} 1341 if err := testutil.DeepEqual(wantFiltered, reply.Filtered); err != nil { 1342 t.Error(err) 1343 } 1344 1345 xr := reply.CrossReferences[ticket] 1346 if xr == nil { 1347 t.Fatalf("Missing expected CrossReferences; found: %#v", reply) 1348 } 1349 sort.Sort(byOffset(xr.Reference)) 1350 1351 if err := testutil.DeepEqual(expected, xr); err != nil { 1352 t.Fatal(err) 1353 } 1354 } 1355 1356 func TestCrossReferencesScoped(t *testing.T) { 1357 ticket := "kythe://someCorpus?lang=otpl#signature" 1358 1359 st := tbl.Construct(t) 1360 reply, err := st.CrossReferences(ctx, &xpb.CrossReferencesRequest{ 1361 Ticket: []string{ticket}, 1362 DefinitionKind: xpb.CrossReferencesRequest_BINDING_DEFINITIONS, 1363 ReferenceKind: xpb.CrossReferencesRequest_ALL_REFERENCES, 1364 Snippets: xpb.SnippetsKind_DEFAULT, 1365 SemanticScopes: true, 1366 }) 1367 testutil.Fatalf(t, "CrossReferencesRequest error: %v", err) 1368 1369 expected := &xpb.CrossReferencesReply_CrossReferenceSet{ 1370 Ticket: ticket, 1371 1372 Reference: []*xpb.CrossReferencesReply_RelatedAnchor{{Anchor: &xpb.Anchor{ 1373 Ticket: "kythe:?path=some/utf16/file#0-4", 1374 Kind: "/kythe/edge/ref", 1375 Parent: "kythe:?path=some/utf16/file", 1376 1377 Span: &cpb.Span{ 1378 Start: &cpb.Point{LineNumber: 1}, 1379 End: &cpb.Point{ByteOffset: 4, LineNumber: 1, ColumnOffset: 4}, 1380 }, 1381 1382 SnippetSpan: &cpb.Span{ 1383 Start: &cpb.Point{ 1384 LineNumber: 1, 1385 }, 1386 End: &cpb.Point{ 1387 ByteOffset: 28, 1388 LineNumber: 1, 1389 ColumnOffset: 28, 1390 }, 1391 }, 1392 Snippet: "これはいくつかのテキストです", 1393 }}, {Anchor: &xpb.Anchor{ 1394 Ticket: "kythe://c?lang=otpl?path=/a/path#51-55", 1395 Kind: "/kythe/edge/ref", 1396 Parent: "kythe://c?path=/a/path", 1397 1398 Span: &cpb.Span{ 1399 Start: &cpb.Point{ 1400 ByteOffset: 51, 1401 LineNumber: 4, 1402 ColumnOffset: 15, 1403 }, 1404 End: &cpb.Point{ 1405 ByteOffset: 55, 1406 LineNumber: 5, 1407 ColumnOffset: 2, 1408 }, 1409 }, 1410 1411 SnippetSpan: &cpb.Span{ 1412 Start: &cpb.Point{ 1413 ByteOffset: 36, 1414 LineNumber: 4, 1415 }, 1416 End: &cpb.Point{ 1417 ByteOffset: 52, 1418 LineNumber: 4, 1419 ColumnOffset: 16, 1420 }, 1421 }, 1422 Snippet: "some random text", 1423 }}}, 1424 1425 Definition: []*xpb.CrossReferencesReply_RelatedAnchor{{ 1426 Ticket: "kythe://someCorpus?lang=otpl#scope", 1427 MarkedSource: &cpb.MarkedSource{ 1428 Kind: cpb.MarkedSource_IDENTIFIER, 1429 PreText: "Scope", 1430 }, 1431 Anchor: &xpb.Anchor{ 1432 Ticket: "kythe://c?lang=otpl?path=/a/path#scope", 1433 Kind: "/kythe/edge/defines/binding", 1434 Parent: "kythe://c?path=/a/path", 1435 BuildConfig: "testConfig", 1436 1437 Span: &cpb.Span{ 1438 Start: &cpb.Point{ 1439 ByteOffset: 27, 1440 LineNumber: 2, 1441 ColumnOffset: 10, 1442 }, 1443 End: &cpb.Point{ 1444 ByteOffset: 33, 1445 LineNumber: 3, 1446 ColumnOffset: 5, 1447 }, 1448 }, 1449 1450 SnippetSpan: &cpb.Span{ 1451 Start: &cpb.Point{ 1452 ByteOffset: 17, 1453 LineNumber: 2, 1454 }, 1455 End: &cpb.Point{ 1456 ByteOffset: 27, 1457 LineNumber: 2, 1458 ColumnOffset: 10, 1459 }, 1460 }, 1461 Snippet: "here and ", 1462 }, 1463 Site: []*xpb.Anchor{{ 1464 Ticket: "kythe://c?lang=otpl?path=/a/path#27-33", 1465 Kind: "/kythe/edge/defines/binding", 1466 Parent: "kythe://c?path=/a/path", 1467 BuildConfig: "testConfig", 1468 1469 Span: &cpb.Span{ 1470 Start: &cpb.Point{ 1471 ByteOffset: 27, 1472 LineNumber: 2, 1473 ColumnOffset: 10, 1474 }, 1475 End: &cpb.Point{ 1476 ByteOffset: 33, 1477 LineNumber: 3, 1478 ColumnOffset: 5, 1479 }, 1480 }, 1481 1482 SnippetSpan: &cpb.Span{ 1483 Start: &cpb.Point{ 1484 ByteOffset: 17, 1485 LineNumber: 2, 1486 }, 1487 End: &cpb.Point{ 1488 ByteOffset: 27, 1489 LineNumber: 2, 1490 ColumnOffset: 10, 1491 }, 1492 }, 1493 Snippet: "here and ", 1494 }}, 1495 }}, 1496 } 1497 1498 if err := testutil.DeepEqual(&xpb.CrossReferencesReply_Total{ 1499 Definitions: 1, 1500 References: 2, 1501 RefEdgeToCount: map[string]int64{ 1502 "/kythe/edge/ref": 2, 1503 }, 1504 }, reply.Total); err != nil { 1505 t.Error(err) 1506 } 1507 wantFiltered := &xpb.CrossReferencesReply_Total{} 1508 if err := testutil.DeepEqual(wantFiltered, reply.Filtered); err != nil { 1509 t.Error(err) 1510 } 1511 1512 xr := reply.CrossReferences[ticket] 1513 if xr == nil { 1514 t.Fatalf("Missing expected CrossReferences; found: %#v", reply) 1515 } 1516 sort.Sort(byOffset(xr.Reference)) 1517 1518 if err := testutil.DeepEqual(expected, xr); err != nil { 1519 t.Fatal(err) 1520 } 1521 } 1522 1523 func TestCrossReferencesPaging(t *testing.T) { 1524 ticket := "kythe://someCorpus?lang=otpl#signature" 1525 1526 st := tbl.Construct(t) 1527 req := &xpb.CrossReferencesRequest{ 1528 Ticket: []string{ticket}, 1529 DefinitionKind: xpb.CrossReferencesRequest_BINDING_DEFINITIONS, 1530 ReferenceKind: xpb.CrossReferencesRequest_ALL_REFERENCES, 1531 Snippets: xpb.SnippetsKind_DEFAULT, 1532 SemanticScopes: true, 1533 PageSize: 1, 1534 } 1535 1536 var anchors []*xpb.CrossReferencesReply_RelatedAnchor 1537 for { 1538 reply, err := st.CrossReferences(ctx, req) 1539 testutil.Fatalf(t, "CrossReferencesRequest error: %v", err) 1540 xr := reply.GetCrossReferences()[ticket] 1541 anchors = append(anchors, xr.GetReference()...) 1542 anchors = append(anchors, xr.GetDefinition()...) 1543 anchors = append(anchors, xr.GetDeclaration()...) 1544 anchors = append(anchors, xr.GetCaller()...) 1545 1546 req.PageToken = reply.GetNextPageToken() 1547 if req.PageToken == "" { 1548 break 1549 } 1550 t.Logf("NextPageToken: %s", req.PageToken) 1551 } 1552 1553 expected := []*xpb.CrossReferencesReply_RelatedAnchor{ 1554 { 1555 Ticket: "kythe://someCorpus?lang=otpl#scope", 1556 MarkedSource: &cpb.MarkedSource{ 1557 Kind: cpb.MarkedSource_IDENTIFIER, 1558 PreText: "Scope", 1559 }, 1560 Anchor: &xpb.Anchor{ 1561 Ticket: "kythe://c?lang=otpl?path=/a/path#scope", 1562 Kind: "/kythe/edge/defines/binding", 1563 Parent: "kythe://c?path=/a/path", 1564 BuildConfig: "testConfig", 1565 1566 Span: &cpb.Span{ 1567 Start: &cpb.Point{ 1568 ByteOffset: 27, 1569 LineNumber: 2, 1570 ColumnOffset: 10, 1571 }, 1572 End: &cpb.Point{ 1573 ByteOffset: 33, 1574 LineNumber: 3, 1575 ColumnOffset: 5, 1576 }, 1577 }, 1578 1579 SnippetSpan: &cpb.Span{ 1580 Start: &cpb.Point{ 1581 ByteOffset: 17, 1582 LineNumber: 2, 1583 }, 1584 End: &cpb.Point{ 1585 ByteOffset: 27, 1586 LineNumber: 2, 1587 ColumnOffset: 10, 1588 }, 1589 }, 1590 Snippet: "here and ", 1591 }, 1592 Site: []*xpb.Anchor{{ 1593 Ticket: "kythe://c?lang=otpl?path=/a/path#27-33", 1594 Kind: "/kythe/edge/defines/binding", 1595 Parent: "kythe://c?path=/a/path", 1596 BuildConfig: "testConfig", 1597 1598 Span: &cpb.Span{ 1599 Start: &cpb.Point{ 1600 ByteOffset: 27, 1601 LineNumber: 2, 1602 ColumnOffset: 10, 1603 }, 1604 End: &cpb.Point{ 1605 ByteOffset: 33, 1606 LineNumber: 3, 1607 ColumnOffset: 5, 1608 }, 1609 }, 1610 1611 SnippetSpan: &cpb.Span{ 1612 Start: &cpb.Point{ 1613 ByteOffset: 17, 1614 LineNumber: 2, 1615 }, 1616 End: &cpb.Point{ 1617 ByteOffset: 27, 1618 LineNumber: 2, 1619 ColumnOffset: 10, 1620 }, 1621 }, 1622 Snippet: "here and ", 1623 }}, 1624 }, 1625 { 1626 Anchor: &xpb.Anchor{ 1627 Ticket: "kythe:?path=some/utf16/file#0-4", 1628 Kind: "/kythe/edge/ref", 1629 Parent: "kythe:?path=some/utf16/file", 1630 1631 Span: &cpb.Span{ 1632 Start: &cpb.Point{LineNumber: 1}, 1633 End: &cpb.Point{ByteOffset: 4, LineNumber: 1, ColumnOffset: 4}, 1634 }, 1635 1636 SnippetSpan: &cpb.Span{ 1637 Start: &cpb.Point{ 1638 LineNumber: 1, 1639 }, 1640 End: &cpb.Point{ 1641 ByteOffset: 28, 1642 LineNumber: 1, 1643 ColumnOffset: 28, 1644 }, 1645 }, 1646 Snippet: "これはいくつかのテキストです", 1647 }, 1648 }, 1649 { 1650 Anchor: &xpb.Anchor{ 1651 Ticket: "kythe://c?lang=otpl?path=/a/path#51-55", 1652 Kind: "/kythe/edge/ref", 1653 Parent: "kythe://c?path=/a/path", 1654 1655 Span: &cpb.Span{ 1656 Start: &cpb.Point{ 1657 ByteOffset: 51, 1658 LineNumber: 4, 1659 ColumnOffset: 15, 1660 }, 1661 End: &cpb.Point{ 1662 ByteOffset: 55, 1663 LineNumber: 5, 1664 ColumnOffset: 2, 1665 }, 1666 }, 1667 1668 SnippetSpan: &cpb.Span{ 1669 Start: &cpb.Point{ 1670 ByteOffset: 36, 1671 LineNumber: 4, 1672 }, 1673 End: &cpb.Point{ 1674 ByteOffset: 52, 1675 LineNumber: 4, 1676 ColumnOffset: 16, 1677 }, 1678 }, 1679 Snippet: "some random text", 1680 }, 1681 }, 1682 } 1683 1684 if err := testutil.DeepEqual(expected, anchors); err != nil { 1685 t.Fatal(err) 1686 } 1687 } 1688 1689 func TestCrossReferencesReadAhead(t *testing.T) { 1690 const flagName = "page_read_ahead" 1691 val := flag.Lookup(flagName).Value.String() 1692 testutil.Fatalf(t, "flag.Set: %v", flag.Set(flagName, "4")) 1693 defer flag.Set(flagName, val) 1694 1695 ticket := "kythe://someCorpus?lang=otpl#signature" 1696 1697 st := tbl.Construct(t) 1698 reply, err := st.CrossReferences(ctx, &xpb.CrossReferencesRequest{ 1699 Ticket: []string{ticket}, 1700 DefinitionKind: xpb.CrossReferencesRequest_BINDING_DEFINITIONS, 1701 ReferenceKind: xpb.CrossReferencesRequest_ALL_REFERENCES, 1702 Snippets: xpb.SnippetsKind_DEFAULT, 1703 }) 1704 testutil.Fatalf(t, "CrossReferencesRequest error: %v", err) 1705 1706 expected := &xpb.CrossReferencesReply_CrossReferenceSet{ 1707 Ticket: ticket, 1708 1709 Reference: []*xpb.CrossReferencesReply_RelatedAnchor{{Anchor: &xpb.Anchor{ 1710 Ticket: "kythe:?path=some/utf16/file#0-4", 1711 Kind: "/kythe/edge/ref", 1712 Parent: "kythe:?path=some/utf16/file", 1713 1714 Span: &cpb.Span{ 1715 Start: &cpb.Point{LineNumber: 1}, 1716 End: &cpb.Point{ByteOffset: 4, LineNumber: 1, ColumnOffset: 4}, 1717 }, 1718 1719 SnippetSpan: &cpb.Span{ 1720 Start: &cpb.Point{ 1721 LineNumber: 1, 1722 }, 1723 End: &cpb.Point{ 1724 ByteOffset: 28, 1725 LineNumber: 1, 1726 ColumnOffset: 28, 1727 }, 1728 }, 1729 Snippet: "これはいくつかのテキストです", 1730 }}, {Anchor: &xpb.Anchor{ 1731 Ticket: "kythe://c?lang=otpl?path=/a/path#51-55", 1732 Kind: "/kythe/edge/ref", 1733 Parent: "kythe://c?path=/a/path", 1734 1735 Span: &cpb.Span{ 1736 Start: &cpb.Point{ 1737 ByteOffset: 51, 1738 LineNumber: 4, 1739 ColumnOffset: 15, 1740 }, 1741 End: &cpb.Point{ 1742 ByteOffset: 55, 1743 LineNumber: 5, 1744 ColumnOffset: 2, 1745 }, 1746 }, 1747 1748 SnippetSpan: &cpb.Span{ 1749 Start: &cpb.Point{ 1750 ByteOffset: 36, 1751 LineNumber: 4, 1752 }, 1753 End: &cpb.Point{ 1754 ByteOffset: 52, 1755 LineNumber: 4, 1756 ColumnOffset: 16, 1757 }, 1758 }, 1759 Snippet: "some random text", 1760 }}}, 1761 1762 Definition: []*xpb.CrossReferencesReply_RelatedAnchor{{Anchor: &xpb.Anchor{ 1763 Ticket: "kythe://c?lang=otpl?path=/a/path#27-33", 1764 Kind: "/kythe/edge/defines/binding", 1765 Parent: "kythe://c?path=/a/path", 1766 BuildConfig: "testConfig", 1767 1768 Span: &cpb.Span{ 1769 Start: &cpb.Point{ 1770 ByteOffset: 27, 1771 LineNumber: 2, 1772 ColumnOffset: 10, 1773 }, 1774 End: &cpb.Point{ 1775 ByteOffset: 33, 1776 LineNumber: 3, 1777 ColumnOffset: 5, 1778 }, 1779 }, 1780 1781 SnippetSpan: &cpb.Span{ 1782 Start: &cpb.Point{ 1783 ByteOffset: 17, 1784 LineNumber: 2, 1785 }, 1786 End: &cpb.Point{ 1787 ByteOffset: 27, 1788 LineNumber: 2, 1789 ColumnOffset: 10, 1790 }, 1791 }, 1792 Snippet: "here and ", 1793 }}}, 1794 } 1795 1796 if err := testutil.DeepEqual(&xpb.CrossReferencesReply_Total{ 1797 Definitions: 1, 1798 References: 2, 1799 RefEdgeToCount: map[string]int64{ 1800 "/kythe/edge/ref": 2, 1801 }, 1802 }, reply.Total); err != nil { 1803 t.Error(err) 1804 } 1805 wantFiltered := &xpb.CrossReferencesReply_Total{} 1806 if err := testutil.DeepEqual(wantFiltered, reply.Filtered); err != nil { 1807 t.Error(err) 1808 } 1809 1810 xr := reply.CrossReferences[ticket] 1811 if xr == nil { 1812 t.Fatalf("Missing expected CrossReferences; found: %#v", reply) 1813 } 1814 sort.Sort(byOffset(xr.Reference)) 1815 1816 if err := testutil.DeepEqual(expected, xr); err != nil { 1817 t.Fatal(err) 1818 } 1819 } 1820 1821 type mockPatcher struct { 1822 files []*srvpb.FileInfo 1823 } 1824 1825 func (p *mockPatcher) Close() error { return nil } 1826 func (p *mockPatcher) AddFile(ctx context.Context, f *srvpb.FileInfo) error { 1827 if f != nil { 1828 p.files = append(p.files, f) 1829 } 1830 return nil 1831 } 1832 func (p *mockPatcher) patchSpan(span *cpb.Span) { 1833 if span == nil { 1834 return 1835 } 1836 // Just move everything over by 1-ish 1837 span.Start.ByteOffset++ 1838 span.Start.LineNumber++ 1839 span.Start.ColumnOffset++ 1840 span.End.ByteOffset++ 1841 span.End.LineNumber++ 1842 span.End.ColumnOffset++ 1843 } 1844 func (p *mockPatcher) patchAnchor(a *xpb.Anchor) { 1845 p.patchSpan(a.Span) 1846 p.patchSpan(a.SnippetSpan) 1847 } 1848 1849 func (p *mockPatcher) PatchAnchors(ctx context.Context, as []*xpb.Anchor) ([]*xpb.Anchor, error) { 1850 for _, a := range as { 1851 p.patchAnchor(a) 1852 } 1853 return as, nil 1854 } 1855 func (p *mockPatcher) PatchRelatedAnchors(ctx context.Context, as []*xpb.CrossReferencesReply_RelatedAnchor) ([]*xpb.CrossReferencesReply_RelatedAnchor, error) { 1856 for _, a := range as { 1857 p.patchAnchor(a.Anchor) 1858 for _, site := range a.Site { 1859 p.patchAnchor(site) 1860 } 1861 } 1862 return as, nil 1863 } 1864 1865 func TestDecorationsPatching(t *testing.T) { 1866 st := tbl.Construct(t) 1867 1868 patcher := &mockPatcher{} 1869 st.MakePatcher = func(ctx context.Context, ws *xpb.Workspace) (MultiFilePatcher, error) { 1870 return patcher, nil 1871 } 1872 1873 reply, err := st.Decorations(ctx, &xpb.DecorationsRequest{ 1874 Location: &xpb.Location{Ticket: "kythe://corpus?path=file/infos"}, 1875 References: true, 1876 TargetDefinitions: true, 1877 1878 Workspace: &xpb.Workspace{Uri: "test:"}, 1879 PatchAgainstWorkspace: true, 1880 }) 1881 testutil.Fatalf(t, "DecorationsRequest error: %v", err) 1882 1883 expected := &xpb.DecorationsReply{ 1884 Location: &xpb.Location{Ticket: "kythe://corpus?path=file/infos"}, 1885 Revision: "overallFileRev", 1886 GeneratedByFile: []*xpb.File{{ 1887 CorpusPath: &cpb.CorpusPath{ 1888 Corpus: "corpus", 1889 Path: "some/proto.proto", 1890 }, 1891 Revision: "generatedRev", 1892 }}, 1893 Reference: []*xpb.DecorationsReply_Reference{{ 1894 TargetTicket: "kythe://corpus?path=another/file", 1895 Kind: "/kythe/edge/includes/ref", 1896 TargetRevision: "anotherFileRev", 1897 Span: &cpb.Span{ 1898 Start: &cpb.Point{ 1899 ByteOffset: 0, 1900 LineNumber: 1, 1901 ColumnOffset: 0, 1902 }, 1903 End: &cpb.Point{ 1904 ByteOffset: 4, 1905 LineNumber: 1, 1906 ColumnOffset: 4, 1907 }, 1908 }, 1909 }, { 1910 TargetTicket: "kythe://corpus?path=def/file#node", 1911 TargetDefinition: "kythe://corpus?path=def/file#anchor", 1912 Kind: "/kythe/edge/ref", 1913 Span: &cpb.Span{ 1914 Start: &cpb.Point{ 1915 ByteOffset: 5, 1916 LineNumber: 1, 1917 ColumnOffset: 5, 1918 }, 1919 End: &cpb.Point{ 1920 ByteOffset: 9, 1921 LineNumber: 1, 1922 ColumnOffset: 9, 1923 }, 1924 }, 1925 }}, 1926 DefinitionLocations: map[string]*xpb.Anchor{ 1927 "kythe://corpus?path=def/file#anchor": &xpb.Anchor{ 1928 Ticket: "kythe://corpus?path=def/file#anchor", 1929 Parent: "kythe://corpus?path=def/file", 1930 Revision: "defFileRev", 1931 Span: &cpb.Span{ 1932 Start: &cpb.Point{ByteOffset: 1, LineNumber: 2, ColumnOffset: 1}, 1933 End: &cpb.Point{ByteOffset: 5, LineNumber: 2, ColumnOffset: 5}, 1934 }, 1935 }, 1936 }, 1937 } 1938 1939 if diff := compare.ProtoDiff(expected, reply); diff != "" { 1940 t.Fatalf("Unexpected diff (- expected; + found):\n%s", diff) 1941 } 1942 } 1943 1944 func TestPatchingError(t *testing.T) { 1945 st := tbl.Construct(t) 1946 1947 // An error when creating a patcher should not result in an overall error. 1948 st.MakePatcher = func(ctx context.Context, ws *xpb.Workspace) (MultiFilePatcher, error) { 1949 return nil, context.Canceled 1950 } 1951 1952 _, err := st.Decorations(ctx, &xpb.DecorationsRequest{ 1953 Location: &xpb.Location{Ticket: "kythe://corpus?path=file/infos"}, 1954 References: true, 1955 TargetDefinitions: true, 1956 1957 Workspace: &xpb.Workspace{Uri: "test:"}, 1958 PatchAgainstWorkspace: true, 1959 }) 1960 testutil.Fatalf(t, "DecorationsRequest error: %v", err) 1961 1962 _, err = st.CrossReferences(ctx, &xpb.CrossReferencesRequest{ 1963 Ticket: []string{"kythe://someCorpus?lang=otpl#signature"}, 1964 DefinitionKind: xpb.CrossReferencesRequest_BINDING_DEFINITIONS, 1965 ReferenceKind: xpb.CrossReferencesRequest_ALL_REFERENCES, 1966 Snippets: xpb.SnippetsKind_DEFAULT, 1967 1968 Workspace: &xpb.Workspace{Uri: "test:"}, 1969 PatchAgainstWorkspace: true, 1970 }) 1971 testutil.Fatalf(t, "CrossReferencesRequest error: %v", err) 1972 1973 _, err = st.Documentation(ctx, &xpb.DocumentationRequest{ 1974 Ticket: []string{"kythe:#documented"}, 1975 1976 IncludeChildren: true, 1977 1978 Workspace: &xpb.Workspace{Uri: "test:"}, 1979 PatchAgainstWorkspace: true, 1980 }) 1981 testutil.Fatalf(t, "DocumentationRequest error: %v", err) 1982 } 1983 1984 func TestCrossReferencesPatching(t *testing.T) { 1985 ticket := "kythe://someCorpus?lang=otpl#signature" 1986 1987 st := tbl.Construct(t) 1988 patcher := &mockPatcher{} 1989 st.MakePatcher = func(ctx context.Context, ws *xpb.Workspace) (MultiFilePatcher, error) { 1990 return patcher, nil 1991 } 1992 reply, err := st.CrossReferences(ctx, &xpb.CrossReferencesRequest{ 1993 Ticket: []string{ticket}, 1994 DefinitionKind: xpb.CrossReferencesRequest_BINDING_DEFINITIONS, 1995 ReferenceKind: xpb.CrossReferencesRequest_ALL_REFERENCES, 1996 Snippets: xpb.SnippetsKind_DEFAULT, 1997 1998 Workspace: &xpb.Workspace{Uri: "test:"}, 1999 PatchAgainstWorkspace: true, 2000 }) 2001 testutil.Fatalf(t, "CrossReferencesRequest error: %v", err) 2002 2003 expected := &xpb.CrossReferencesReply_CrossReferenceSet{ 2004 Ticket: ticket, 2005 2006 Reference: []*xpb.CrossReferencesReply_RelatedAnchor{{Anchor: &xpb.Anchor{ 2007 Ticket: "kythe:?path=some/utf16/file#0-4", 2008 Kind: "/kythe/edge/ref", 2009 Parent: "kythe:?path=some/utf16/file", 2010 2011 Span: &cpb.Span{ 2012 Start: &cpb.Point{ByteOffset: 1, LineNumber: 2, ColumnOffset: 1}, 2013 End: &cpb.Point{ByteOffset: 5, LineNumber: 2, ColumnOffset: 5}, 2014 }, 2015 2016 SnippetSpan: &cpb.Span{ 2017 Start: &cpb.Point{ 2018 ByteOffset: 1, 2019 LineNumber: 2, 2020 ColumnOffset: 1, 2021 }, 2022 End: &cpb.Point{ 2023 ByteOffset: 29, 2024 LineNumber: 2, 2025 ColumnOffset: 29, 2026 }, 2027 }, 2028 Snippet: "これはいくつかのテキストです", 2029 }}, {Anchor: &xpb.Anchor{ 2030 Ticket: "kythe://c?lang=otpl?path=/a/path#51-55", 2031 Kind: "/kythe/edge/ref", 2032 Parent: "kythe://c?path=/a/path", 2033 2034 Span: &cpb.Span{ 2035 Start: &cpb.Point{ 2036 ByteOffset: 52, 2037 LineNumber: 5, 2038 ColumnOffset: 16, 2039 }, 2040 End: &cpb.Point{ 2041 ByteOffset: 56, 2042 LineNumber: 6, 2043 ColumnOffset: 3, 2044 }, 2045 }, 2046 2047 SnippetSpan: &cpb.Span{ 2048 Start: &cpb.Point{ 2049 ByteOffset: 37, 2050 LineNumber: 5, 2051 ColumnOffset: 1, 2052 }, 2053 End: &cpb.Point{ 2054 ByteOffset: 53, 2055 LineNumber: 5, 2056 ColumnOffset: 17, 2057 }, 2058 }, 2059 Snippet: "some random text", 2060 }}}, 2061 2062 Definition: []*xpb.CrossReferencesReply_RelatedAnchor{{Anchor: &xpb.Anchor{ 2063 Ticket: "kythe://c?lang=otpl?path=/a/path#27-33", 2064 Kind: "/kythe/edge/defines/binding", 2065 Parent: "kythe://c?path=/a/path", 2066 BuildConfig: "testConfig", 2067 2068 Span: &cpb.Span{ 2069 Start: &cpb.Point{ 2070 ByteOffset: 28, 2071 LineNumber: 3, 2072 ColumnOffset: 11, 2073 }, 2074 End: &cpb.Point{ 2075 ByteOffset: 34, 2076 LineNumber: 4, 2077 ColumnOffset: 6, 2078 }, 2079 }, 2080 2081 SnippetSpan: &cpb.Span{ 2082 Start: &cpb.Point{ 2083 ByteOffset: 18, 2084 LineNumber: 3, 2085 ColumnOffset: 1, 2086 }, 2087 End: &cpb.Point{ 2088 ByteOffset: 28, 2089 LineNumber: 3, 2090 ColumnOffset: 11, 2091 }, 2092 }, 2093 Snippet: "here and ", 2094 }}}, 2095 } 2096 var expectedInfos []*srvpb.FileInfo 2097 2098 xr := reply.CrossReferences[ticket] 2099 if xr == nil { 2100 t.Fatalf("Missing expected CrossReferences; found: %#v", reply) 2101 } 2102 sort.Sort(byOffset(xr.Reference)) 2103 2104 if err := testutil.DeepEqual(expected, xr); err != nil { 2105 t.Fatal(err) 2106 } 2107 if err := testutil.DeepEqual(expectedInfos, patcher.files); err != nil { 2108 t.Fatal(err) 2109 } 2110 } 2111 2112 func TestCrossReferencesFiltering(t *testing.T) { 2113 ticket := "kythe://someCorpus?lang=otpl#signature" 2114 2115 st := tbl.Construct(t) 2116 reply, err := st.CrossReferences(ctx, &xpb.CrossReferencesRequest{ 2117 Ticket: []string{ticket}, 2118 DefinitionKind: xpb.CrossReferencesRequest_BINDING_DEFINITIONS, 2119 ReferenceKind: xpb.CrossReferencesRequest_ALL_REFERENCES, 2120 Snippets: xpb.SnippetsKind_DEFAULT, 2121 2122 CorpusPathFilters: mustParseFilters(` 2123 filter: { 2124 type: INCLUDE_ONLY 2125 corpus: "^c$" 2126 } 2127 `), 2128 }) 2129 testutil.Fatalf(t, "CrossReferencesRequest error: %v", err) 2130 2131 expected := &xpb.CrossReferencesReply_CrossReferenceSet{ 2132 Ticket: ticket, 2133 2134 Reference: []*xpb.CrossReferencesReply_RelatedAnchor{{Anchor: &xpb.Anchor{ 2135 Ticket: "kythe://c?lang=otpl?path=/a/path#51-55", 2136 Kind: "/kythe/edge/ref", 2137 Parent: "kythe://c?path=/a/path", 2138 2139 Span: &cpb.Span{ 2140 Start: &cpb.Point{ 2141 ByteOffset: 51, 2142 LineNumber: 4, 2143 ColumnOffset: 15, 2144 }, 2145 End: &cpb.Point{ 2146 ByteOffset: 55, 2147 LineNumber: 5, 2148 ColumnOffset: 2, 2149 }, 2150 }, 2151 2152 SnippetSpan: &cpb.Span{ 2153 Start: &cpb.Point{ 2154 ByteOffset: 36, 2155 LineNumber: 4, 2156 }, 2157 End: &cpb.Point{ 2158 ByteOffset: 52, 2159 LineNumber: 4, 2160 ColumnOffset: 16, 2161 }, 2162 }, 2163 Snippet: "some random text", 2164 }}}, 2165 2166 Definition: []*xpb.CrossReferencesReply_RelatedAnchor{{Anchor: &xpb.Anchor{ 2167 Ticket: "kythe://c?lang=otpl?path=/a/path#27-33", 2168 Kind: "/kythe/edge/defines/binding", 2169 Parent: "kythe://c?path=/a/path", 2170 BuildConfig: "testConfig", 2171 2172 Span: &cpb.Span{ 2173 Start: &cpb.Point{ 2174 ByteOffset: 27, 2175 LineNumber: 2, 2176 ColumnOffset: 10, 2177 }, 2178 End: &cpb.Point{ 2179 ByteOffset: 33, 2180 LineNumber: 3, 2181 ColumnOffset: 5, 2182 }, 2183 }, 2184 2185 SnippetSpan: &cpb.Span{ 2186 Start: &cpb.Point{ 2187 ByteOffset: 17, 2188 LineNumber: 2, 2189 }, 2190 End: &cpb.Point{ 2191 ByteOffset: 27, 2192 LineNumber: 2, 2193 ColumnOffset: 10, 2194 }, 2195 }, 2196 Snippet: "here and ", 2197 }}}, 2198 } 2199 2200 if err := testutil.DeepEqual(&xpb.CrossReferencesReply_Total{ 2201 Definitions: 1, 2202 References: 1, 2203 RefEdgeToCount: map[string]int64{ 2204 "/kythe/edge/ref": 1, 2205 }, 2206 }, reply.Total); err != nil { 2207 t.Error(err) 2208 } 2209 if err := testutil.DeepEqual(&xpb.CrossReferencesReply_Total{ 2210 References: 1, 2211 RefEdgeToCount: map[string]int64{ 2212 "/kythe/edge/ref": 1, 2213 }, 2214 }, reply.Filtered); err != nil { 2215 t.Error(err) 2216 } 2217 2218 xr := reply.CrossReferences[ticket] 2219 if xr == nil { 2220 t.Fatalf("Missing expected CrossReferences; found: %s", reply) 2221 } 2222 sort.Sort(byOffset(xr.Reference)) 2223 2224 if diff := compare.ProtoDiff(expected, xr); diff != "" { 2225 t.Fatalf("(-expected; +found):\n%s", diff) 2226 } 2227 } 2228 2229 func TestCrossReferences_BuildConfigRefs(t *testing.T) { 2230 ticket := "kythe://someCorpus?lang=otpl#signature" 2231 2232 st := tbl.Construct(t) 2233 reply, err := st.CrossReferences(ctx, &xpb.CrossReferencesRequest{ 2234 Ticket: []string{ticket}, 2235 DefinitionKind: xpb.CrossReferencesRequest_ALL_DEFINITIONS, 2236 ReferenceKind: xpb.CrossReferencesRequest_ALL_REFERENCES, 2237 Snippets: xpb.SnippetsKind_DEFAULT, 2238 BuildConfig: []string{"testConfig"}, 2239 }) 2240 testutil.Fatalf(t, "CrossReferencesRequest error: %v", err) 2241 2242 expected := &xpb.CrossReferencesReply_CrossReferenceSet{ 2243 Ticket: ticket, 2244 2245 Definition: []*xpb.CrossReferencesReply_RelatedAnchor{{Anchor: &xpb.Anchor{ 2246 Ticket: "kythe://c?lang=otpl?path=/a/path#27-33", 2247 Kind: "/kythe/edge/defines/binding", 2248 Parent: "kythe://c?path=/a/path", 2249 BuildConfig: "testConfig", 2250 2251 Span: &cpb.Span{ 2252 Start: &cpb.Point{ 2253 ByteOffset: 27, 2254 LineNumber: 2, 2255 ColumnOffset: 10, 2256 }, 2257 End: &cpb.Point{ 2258 ByteOffset: 33, 2259 LineNumber: 3, 2260 ColumnOffset: 5, 2261 }, 2262 }, 2263 2264 SnippetSpan: &cpb.Span{ 2265 Start: &cpb.Point{ 2266 ByteOffset: 17, 2267 LineNumber: 2, 2268 }, 2269 End: &cpb.Point{ 2270 ByteOffset: 27, 2271 LineNumber: 2, 2272 ColumnOffset: 10, 2273 }, 2274 }, 2275 Snippet: "here and ", 2276 }}}, 2277 } 2278 2279 if err := testutil.DeepEqual(&xpb.CrossReferencesReply_Total{ 2280 Definitions: 1, 2281 }, reply.Total); err != nil { 2282 t.Error(err) 2283 } 2284 if err := testutil.DeepEqual(&xpb.CrossReferencesReply_Total{}, reply.Filtered); err != nil { 2285 t.Error(err) 2286 } 2287 2288 xr := reply.CrossReferences[ticket] 2289 if xr == nil { 2290 t.Fatalf("Missing expected CrossReferences; found: %#v", reply) 2291 } 2292 sort.Sort(byOffset(xr.Reference)) 2293 2294 if err := testutil.DeepEqual(expected, xr); err != nil { 2295 t.Fatal(err) 2296 } 2297 } 2298 2299 func TestCrossReferencesRelatedNodes(t *testing.T) { 2300 ticket := "kythe://someCorpus?lang=otpl#withRelated" 2301 2302 st := tbl.Construct(t) 2303 reply, err := st.CrossReferences(ctx, &xpb.CrossReferencesRequest{ 2304 Ticket: []string{ticket}, 2305 Filter: []string{"**"}, 2306 }) 2307 testutil.Fatalf(t, "CrossReferencesRequest error: %v", err) 2308 2309 expected := &xpb.CrossReferencesReply_CrossReferenceSet{ 2310 Ticket: ticket, 2311 MarkedSource: &cpb.MarkedSource{ 2312 Kind: cpb.MarkedSource_IDENTIFIER, 2313 PreText: "id", 2314 }, 2315 2316 RelatedNode: []*xpb.CrossReferencesReply_RelatedNode{{ 2317 Ticket: "kythe:#someRelatedNode", 2318 RelationKind: "/kythe/edge/extends", 2319 }, { 2320 Ticket: "kythe:#someParameter0", 2321 RelationKind: "/kythe/edge/param", 2322 Ordinal: 0, 2323 }, { 2324 Ticket: "kythe:#someParameter1", 2325 RelationKind: "/kythe/edge/param", 2326 Ordinal: 1, 2327 }}, 2328 } 2329 expectedNodes := nodeInfos(getNodes( 2330 ticket, 2331 "kythe:#someRelatedNode", 2332 "kythe:#someParameter0", 2333 "kythe:#someParameter1")) 2334 2335 if err := testutil.DeepEqual(&xpb.CrossReferencesReply_Total{ 2336 RelatedNodesByRelation: map[string]int64{ 2337 "/kythe/edge/extends": 1, 2338 "/kythe/edge/param": 2, 2339 }, 2340 }, reply.Total); err != nil { 2341 t.Error(err) 2342 } 2343 2344 xr := reply.CrossReferences[ticket] 2345 if xr == nil { 2346 t.Fatalf("Missing expected CrossReferences; found: %#v", reply) 2347 } else if err := testutil.DeepEqual(expected, xr); err != nil { 2348 t.Fatal(err) 2349 } else if err := testutil.DeepEqual(expectedNodes, reply.Nodes); err != nil { 2350 t.Fatal(err) 2351 } 2352 } 2353 2354 func TestCrossReferencesMarkedSource(t *testing.T) { 2355 const ticket = "kythe://someCorpus?lang=otpl#withRelated" 2356 2357 st := tbl.Construct(t) 2358 reply, err := st.CrossReferences(ctx, &xpb.CrossReferencesRequest{ 2359 Ticket: []string{ticket}, 2360 Filter: []string{"**"}, 2361 }) 2362 testutil.Fatalf(t, "CrossReferencesRequest error: %v", err) 2363 2364 expected := &xpb.CrossReferencesReply_CrossReferenceSet{ 2365 Ticket: ticket, 2366 MarkedSource: &cpb.MarkedSource{ 2367 Kind: cpb.MarkedSource_IDENTIFIER, 2368 PreText: "id", 2369 }, 2370 2371 RelatedNode: []*xpb.CrossReferencesReply_RelatedNode{{ 2372 Ticket: "kythe:#someRelatedNode", 2373 RelationKind: "/kythe/edge/extends", 2374 }, { 2375 Ticket: "kythe:#someParameter0", 2376 RelationKind: "/kythe/edge/param", 2377 Ordinal: 0, 2378 }, { 2379 Ticket: "kythe:#someParameter1", 2380 RelationKind: "/kythe/edge/param", 2381 Ordinal: 1, 2382 }}, 2383 } 2384 2385 if err := testutil.DeepEqual(&xpb.CrossReferencesReply_Total{ 2386 RelatedNodesByRelation: map[string]int64{ 2387 "/kythe/edge/extends": 1, 2388 "/kythe/edge/param": 2, 2389 }, 2390 }, reply.Total); err != nil { 2391 t.Error(err) 2392 } 2393 2394 xr := reply.CrossReferences[ticket] 2395 if xr == nil { 2396 t.Fatalf("Missing expected CrossReferences; found: %#v", reply) 2397 } else if err := testutil.DeepEqual(expected, xr); err != nil { 2398 t.Fatal(err) 2399 } 2400 } 2401 2402 func TestCrossReferencesMerge(t *testing.T) { 2403 ticket := "kythe://someCorpus?lang=otpl#withMerge" 2404 2405 st := tbl.Construct(t) 2406 reply, err := st.CrossReferences(ctx, &xpb.CrossReferencesRequest{ 2407 Ticket: []string{ticket}, 2408 CallerKind: xpb.CrossReferencesRequest_DIRECT_CALLERS, 2409 Filter: []string{"**"}, 2410 }) 2411 testutil.Fatalf(t, "CrossReferencesRequest error: %v", err) 2412 2413 expected := &xpb.CrossReferencesReply_CrossReferenceSet{ 2414 Ticket: ticket, 2415 MarkedSource: &cpb.MarkedSource{ 2416 Kind: cpb.MarkedSource_IDENTIFIER, 2417 PreText: "id", 2418 }, 2419 2420 Caller: []*xpb.CrossReferencesReply_RelatedAnchor{{ 2421 Anchor: &xpb.Anchor{ 2422 Ticket: "kythe:?path=someFile#someCallerAnchor", 2423 Parent: "kythe:?path=someFile", 2424 Span: arbitrarySpan, 2425 }, 2426 Ticket: "kythe:#someCaller", 2427 MarkedSource: &cpb.MarkedSource{ 2428 Kind: cpb.MarkedSource_IDENTIFIER, 2429 PreText: "id", 2430 }, 2431 Site: []*xpb.Anchor{{ 2432 Ticket: "kythe:?path=someFile#someCallsiteAnchor", 2433 Parent: "kythe:?path=someFile", 2434 }}, 2435 }}, 2436 RelatedNode: []*xpb.CrossReferencesReply_RelatedNode{{ 2437 Ticket: "kythe:#someRelatedNode", 2438 RelationKind: "/kythe/edge/extends", 2439 }, { 2440 Ticket: "kythe:#someParameter0", 2441 RelationKind: "/kythe/edge/param", 2442 Ordinal: 0, 2443 }, { 2444 Ticket: "kythe:#someParameter1", 2445 RelationKind: "/kythe/edge/param", 2446 Ordinal: 1, 2447 }}, 2448 } 2449 2450 if err := testutil.DeepEqual(&xpb.CrossReferencesReply_Total{ 2451 Callers: 1, 2452 RelatedNodesByRelation: map[string]int64{ 2453 "/kythe/edge/extends": 1, 2454 "/kythe/edge/param": 2, 2455 }, 2456 }, reply.Total); err != nil { 2457 t.Error(err) 2458 } 2459 2460 xr := reply.CrossReferences[ticket] 2461 if xr == nil { 2462 t.Fatalf("Missing expected CrossReferences; found: %#v", reply) 2463 } else if err := testutil.DeepEqual(expected, xr); err != nil { 2464 t.Fatal(err) 2465 } 2466 } 2467 2468 func TestCrossReferencesIndirection(t *testing.T) { 2469 ticket := "kythe:#aliasNode" 2470 st := tbl.Construct(t) 2471 2472 t.Run("none", func(t *testing.T) { 2473 experimentalCrossReferenceIndirectionKinds = nil 2474 2475 reply, err := st.CrossReferences(ctx, &xpb.CrossReferencesRequest{ 2476 Ticket: []string{ticket}, 2477 ReferenceKind: xpb.CrossReferencesRequest_ALL_REFERENCES, 2478 }) 2479 testutil.Fatalf(t, "CrossReferencesRequest error: %v", err) 2480 2481 expected := &xpb.CrossReferencesReply_CrossReferenceSet{ 2482 Ticket: ticket, 2483 2484 Reference: []*xpb.CrossReferencesReply_RelatedAnchor{{Anchor: &xpb.Anchor{ 2485 Ticket: "kythe:?path=somewhere#0-9", 2486 Kind: "/kythe/edge/ref", 2487 Parent: "kythe:?path=somewhere", 2488 2489 Span: &cpb.Span{ 2490 Start: &cpb.Point{LineNumber: 1}, 2491 End: &cpb.Point{ByteOffset: 9, LineNumber: 1, ColumnOffset: 9}, 2492 }, 2493 }}}, 2494 } 2495 2496 if err := testutil.DeepEqual(&xpb.CrossReferencesReply_Total{ 2497 References: 1, 2498 RefEdgeToCount: map[string]int64{ 2499 "/kythe/edge/ref": 1, 2500 }, 2501 }, reply.Total); err != nil { 2502 t.Error(err) 2503 } 2504 2505 xr := reply.CrossReferences[ticket] 2506 if xr == nil { 2507 t.Fatalf("Missing expected CrossReferences; found: %#v", reply) 2508 } else if err := testutil.DeepEqual(expected, xr); err != nil { 2509 t.Fatal(err) 2510 } 2511 }) 2512 2513 t.Run("talias", func(t *testing.T) { 2514 // Enable indirection for talias nodes. 2515 experimentalCrossReferenceIndirectionKinds = nil 2516 experimentalCrossReferenceIndirectionKinds.Set("talias=%/kythe/edge/aliases") 2517 2518 reply, err := st.CrossReferences(ctx, &xpb.CrossReferencesRequest{ 2519 Ticket: []string{ticket}, 2520 ReferenceKind: xpb.CrossReferencesRequest_ALL_REFERENCES, 2521 }) 2522 testutil.Fatalf(t, "CrossReferencesRequest error: %v", err) 2523 2524 expected := &xpb.CrossReferencesReply_CrossReferenceSet{ 2525 Ticket: ticket, 2526 2527 Reference: []*xpb.CrossReferencesReply_RelatedAnchor{{Anchor: &xpb.Anchor{ 2528 Ticket: "kythe:?path=somewhere#0-9", 2529 Kind: "/kythe/edge/ref", 2530 Parent: "kythe:?path=somewhere", 2531 2532 Span: &cpb.Span{ 2533 Start: &cpb.Point{LineNumber: 1}, 2534 End: &cpb.Point{ByteOffset: 9, LineNumber: 1, ColumnOffset: 9}, 2535 }, 2536 }}, {Anchor: &xpb.Anchor{ 2537 Ticket: "kythe:?path=some/utf16/file#0-4", 2538 Kind: "/kythe/edge/ref", 2539 Parent: "kythe:?path=some/utf16/file", 2540 2541 Span: &cpb.Span{ 2542 Start: &cpb.Point{LineNumber: 1}, 2543 End: &cpb.Point{ByteOffset: 4, LineNumber: 1, ColumnOffset: 4}, 2544 }, 2545 }}, {Anchor: &xpb.Anchor{ 2546 Ticket: "kythe://c?lang=otpl?path=/a/path#51-55", 2547 Kind: "/kythe/edge/ref", 2548 Parent: "kythe://c?path=/a/path", 2549 2550 Span: &cpb.Span{ 2551 Start: &cpb.Point{ 2552 ByteOffset: 51, 2553 LineNumber: 4, 2554 ColumnOffset: 15, 2555 }, 2556 End: &cpb.Point{ 2557 ByteOffset: 55, 2558 LineNumber: 5, 2559 ColumnOffset: 2, 2560 }, 2561 }, 2562 }}}, 2563 } 2564 2565 if err := testutil.DeepEqual(&xpb.CrossReferencesReply_Total{ 2566 References: 3, 2567 RefEdgeToCount: map[string]int64{ 2568 "/kythe/edge/ref": 3, 2569 }, 2570 }, reply.Total); err != nil { 2571 t.Error(err) 2572 } 2573 2574 xr := reply.CrossReferences[ticket] 2575 if xr == nil { 2576 t.Fatalf("Missing expected CrossReferences; found: %#v", reply) 2577 } 2578 2579 sort.Sort(byOffset(xr.Reference)) 2580 if err := testutil.DeepEqual(expected, xr); err != nil { 2581 t.Fatal(err) 2582 } 2583 }) 2584 2585 t.Run("wildcard", func(t *testing.T) { 2586 // Enable indirection for any nodes with a reverse aliases edge. 2587 experimentalCrossReferenceIndirectionKinds = nil 2588 experimentalCrossReferenceIndirectionKinds.Set("*=%/kythe/edge/aliases") 2589 2590 reply, err := st.CrossReferences(ctx, &xpb.CrossReferencesRequest{ 2591 Ticket: []string{ticket}, 2592 ReferenceKind: xpb.CrossReferencesRequest_ALL_REFERENCES, 2593 }) 2594 testutil.Fatalf(t, "CrossReferencesRequest error: %v", err) 2595 2596 expected := &xpb.CrossReferencesReply_CrossReferenceSet{ 2597 Ticket: ticket, 2598 2599 Reference: []*xpb.CrossReferencesReply_RelatedAnchor{{Anchor: &xpb.Anchor{ 2600 Ticket: "kythe:?path=somewhere#0-9", 2601 Kind: "/kythe/edge/ref", 2602 Parent: "kythe:?path=somewhere", 2603 2604 Span: &cpb.Span{ 2605 Start: &cpb.Point{LineNumber: 1}, 2606 End: &cpb.Point{ByteOffset: 9, LineNumber: 1, ColumnOffset: 9}, 2607 }, 2608 }}, {Anchor: &xpb.Anchor{ 2609 Ticket: "kythe:?path=some/utf16/file#0-4", 2610 Kind: "/kythe/edge/ref", 2611 Parent: "kythe:?path=some/utf16/file", 2612 2613 Span: &cpb.Span{ 2614 Start: &cpb.Point{LineNumber: 1}, 2615 End: &cpb.Point{ByteOffset: 4, LineNumber: 1, ColumnOffset: 4}, 2616 }, 2617 }}, {Anchor: &xpb.Anchor{ 2618 Ticket: "kythe://c?lang=otpl?path=/a/path#51-55", 2619 Kind: "/kythe/edge/ref", 2620 Parent: "kythe://c?path=/a/path", 2621 2622 Span: &cpb.Span{ 2623 Start: &cpb.Point{ 2624 ByteOffset: 51, 2625 LineNumber: 4, 2626 ColumnOffset: 15, 2627 }, 2628 End: &cpb.Point{ 2629 ByteOffset: 55, 2630 LineNumber: 5, 2631 ColumnOffset: 2, 2632 }, 2633 }, 2634 }}}, 2635 } 2636 2637 if err := testutil.DeepEqual(&xpb.CrossReferencesReply_Total{ 2638 References: 3, 2639 RefEdgeToCount: map[string]int64{ 2640 "/kythe/edge/ref": 3, 2641 }, 2642 }, reply.Total); err != nil { 2643 t.Error(err) 2644 } 2645 2646 xr := reply.CrossReferences[ticket] 2647 if xr == nil { 2648 t.Fatalf("Missing expected CrossReferences; found: %#v", reply) 2649 } 2650 2651 sort.Sort(byOffset(xr.Reference)) 2652 if err := testutil.DeepEqual(expected, xr); err != nil { 2653 t.Fatal(err) 2654 } 2655 }) 2656 2657 t.Run("single_indirect", func(t *testing.T) { 2658 // Enable single indirection for talias nodes. 2659 experimentalCrossReferenceIndirectionKinds = nil 2660 experimentalCrossReferenceIndirectionKinds.Set("talias=/kythe/edge/indirect") 2661 2662 reply, err := st.CrossReferences(ctx, &xpb.CrossReferencesRequest{ 2663 Ticket: []string{ticket}, 2664 ReferenceKind: xpb.CrossReferencesRequest_ALL_REFERENCES, 2665 }) 2666 testutil.Fatalf(t, "CrossReferencesRequest error: %v", err) 2667 2668 expected := &xpb.CrossReferencesReply_CrossReferenceSet{ 2669 Ticket: ticket, 2670 2671 Reference: []*xpb.CrossReferencesReply_RelatedAnchor{{Anchor: &xpb.Anchor{ 2672 Ticket: "kythe:?path=somewhere#0-9", 2673 Kind: "/kythe/edge/ref", 2674 Parent: "kythe:?path=somewhere", 2675 2676 Span: &cpb.Span{ 2677 Start: &cpb.Point{LineNumber: 1}, 2678 End: &cpb.Point{ByteOffset: 9, LineNumber: 1, ColumnOffset: 9}, 2679 }, 2680 }}, {Anchor: &xpb.Anchor{ 2681 Ticket: "kythe:?path=somewhereElse#0-9", 2682 Kind: "/kythe/edge/ref", 2683 Parent: "kythe:?path=somewhereElse", 2684 2685 Span: &cpb.Span{ 2686 Start: &cpb.Point{LineNumber: 1}, 2687 End: &cpb.Point{ByteOffset: 9, LineNumber: 1, ColumnOffset: 9}, 2688 }, 2689 }}}, 2690 } 2691 2692 if err := testutil.DeepEqual(&xpb.CrossReferencesReply_Total{ 2693 References: 2, 2694 RefEdgeToCount: map[string]int64{ 2695 "/kythe/edge/ref": 2, 2696 }, 2697 }, reply.Total); err != nil { 2698 t.Error(err) 2699 } 2700 2701 xr := reply.CrossReferences[ticket] 2702 if xr == nil { 2703 t.Fatalf("Missing expected CrossReferences; found: %#v", reply) 2704 } 2705 2706 sort.Sort(byOffset(xr.Reference)) 2707 if err := testutil.DeepEqual(expected, xr); err != nil { 2708 t.Fatal(err) 2709 } 2710 }) 2711 2712 t.Run("double_indirect", func(t *testing.T) { 2713 // Enable double indirection for talias nodes. 2714 experimentalCrossReferenceIndirectionKinds = nil 2715 experimentalCrossReferenceIndirectionKinds.Set("talias=/kythe/edge/indirect,indirect=/kythe/edge/indirect") 2716 2717 reply, err := st.CrossReferences(ctx, &xpb.CrossReferencesRequest{ 2718 Ticket: []string{ticket}, 2719 ReferenceKind: xpb.CrossReferencesRequest_ALL_REFERENCES, 2720 }) 2721 testutil.Fatalf(t, "CrossReferencesRequest error: %v", err) 2722 2723 expected := &xpb.CrossReferencesReply_CrossReferenceSet{ 2724 Ticket: ticket, 2725 2726 Reference: []*xpb.CrossReferencesReply_RelatedAnchor{{Anchor: &xpb.Anchor{ 2727 Ticket: "kythe:?path=somewhere#0-9", 2728 Kind: "/kythe/edge/ref", 2729 Parent: "kythe:?path=somewhere", 2730 2731 Span: &cpb.Span{ 2732 Start: &cpb.Point{LineNumber: 1}, 2733 End: &cpb.Point{ByteOffset: 9, LineNumber: 1, ColumnOffset: 9}, 2734 }, 2735 }}, {Anchor: &xpb.Anchor{ 2736 Ticket: "kythe:?path=somewhereElse#0-9", 2737 Kind: "/kythe/edge/ref", 2738 Parent: "kythe:?path=somewhereElse", 2739 2740 Span: &cpb.Span{ 2741 Start: &cpb.Point{LineNumber: 1}, 2742 End: &cpb.Point{ByteOffset: 9, LineNumber: 1, ColumnOffset: 9}, 2743 }, 2744 }}, {Anchor: &xpb.Anchor{ 2745 Ticket: "kythe:?path=some/utf16/file#0-4", 2746 Kind: "/kythe/edge/ref", 2747 Parent: "kythe:?path=some/utf16/file", 2748 2749 Span: &cpb.Span{ 2750 Start: &cpb.Point{LineNumber: 1}, 2751 End: &cpb.Point{ByteOffset: 4, LineNumber: 1, ColumnOffset: 4}, 2752 }, 2753 }}, {Anchor: &xpb.Anchor{ 2754 Ticket: "kythe://c?lang=otpl?path=/a/path#51-55", 2755 Kind: "/kythe/edge/ref", 2756 Parent: "kythe://c?path=/a/path", 2757 2758 Span: &cpb.Span{ 2759 Start: &cpb.Point{ 2760 ByteOffset: 51, 2761 LineNumber: 4, 2762 ColumnOffset: 15, 2763 }, 2764 End: &cpb.Point{ 2765 ByteOffset: 55, 2766 LineNumber: 5, 2767 ColumnOffset: 2, 2768 }, 2769 }, 2770 }}}, 2771 } 2772 2773 if err := testutil.DeepEqual(&xpb.CrossReferencesReply_Total{ 2774 References: 4, 2775 RefEdgeToCount: map[string]int64{ 2776 "/kythe/edge/ref": 4, 2777 }, 2778 }, reply.Total); err != nil { 2779 t.Error(err) 2780 } 2781 2782 xr := reply.CrossReferences[ticket] 2783 if xr == nil { 2784 t.Fatalf("Missing expected CrossReferences; found: %#v", reply) 2785 } 2786 2787 sort.Sort(byOffset(xr.Reference)) 2788 if err := testutil.DeepEqual(expected, xr); err != nil { 2789 t.Fatal(err) 2790 } 2791 }) 2792 } 2793 2794 func TestCrossReferencesDirectCallers(t *testing.T) { 2795 ticket := "kythe://someCorpus?lang=otpl#withCallers" 2796 2797 st := tbl.Construct(t) 2798 reply, err := st.CrossReferences(ctx, &xpb.CrossReferencesRequest{ 2799 Ticket: []string{ticket}, 2800 CallerKind: xpb.CrossReferencesRequest_DIRECT_CALLERS, 2801 }) 2802 testutil.Fatalf(t, "CrossReferencesRequest error: %v", err) 2803 2804 expected := &xpb.CrossReferencesReply_CrossReferenceSet{ 2805 Ticket: ticket, 2806 2807 Caller: []*xpb.CrossReferencesReply_RelatedAnchor{{ 2808 Anchor: &xpb.Anchor{ 2809 Ticket: "kythe:?path=someFile#someCallerAnchor", 2810 Parent: "kythe:?path=someFile", 2811 Span: arbitrarySpan, 2812 }, 2813 Ticket: "kythe:#someCaller", 2814 MarkedSource: &cpb.MarkedSource{ 2815 Kind: cpb.MarkedSource_IDENTIFIER, 2816 PreText: "id", 2817 }, 2818 Site: []*xpb.Anchor{{ 2819 Ticket: "kythe:?path=someFile#someCallsiteAnchor", 2820 Parent: "kythe:?path=someFile", 2821 }}, 2822 }}, 2823 } 2824 2825 if err := testutil.DeepEqual(&xpb.CrossReferencesReply_Total{ 2826 Callers: 1, 2827 }, reply.Total); err != nil { 2828 t.Error(err) 2829 } 2830 2831 xr := reply.CrossReferences[ticket] 2832 if xr == nil { 2833 t.Fatalf("Missing expected CrossReferences; found: %#v", reply) 2834 } else if err := testutil.DeepEqual(expected, xr); err != nil { 2835 t.Fatal(err) 2836 } 2837 } 2838 2839 func TestCrossReferencesOverrideCallers(t *testing.T) { 2840 ticket := "kythe://someCorpus?lang=otpl#withCallers" 2841 2842 st := tbl.Construct(t) 2843 reply, err := st.CrossReferences(ctx, &xpb.CrossReferencesRequest{ 2844 Ticket: []string{ticket}, 2845 CallerKind: xpb.CrossReferencesRequest_OVERRIDE_CALLERS, 2846 }) 2847 testutil.Fatalf(t, "CrossReferencesRequest error: %v", err) 2848 2849 expected := &xpb.CrossReferencesReply_CrossReferenceSet{ 2850 Ticket: ticket, 2851 2852 Caller: []*xpb.CrossReferencesReply_RelatedAnchor{{ 2853 Anchor: &xpb.Anchor{ 2854 Ticket: "kythe:?path=someFile#someCallerAnchor", 2855 Parent: "kythe:?path=someFile", 2856 Span: arbitrarySpan, 2857 }, 2858 Ticket: "kythe:#someCaller", 2859 MarkedSource: &cpb.MarkedSource{ 2860 Kind: cpb.MarkedSource_IDENTIFIER, 2861 PreText: "id", 2862 }, 2863 Site: []*xpb.Anchor{{ 2864 Ticket: "kythe:?path=someFile#someCallsiteAnchor", 2865 Parent: "kythe:?path=someFile", 2866 }}, 2867 }, { 2868 Speculative: true, 2869 Anchor: &xpb.Anchor{ 2870 Ticket: "kythe:?path=someFile#someOverrideCallerAnchor1", 2871 Parent: "kythe:?path=someFile", 2872 Span: arbitrarySpan, 2873 }, 2874 Site: []*xpb.Anchor{{ 2875 Ticket: "kythe:?path=someFile#someCallsiteAnchor", 2876 Parent: "kythe:?path=someFile", 2877 Span: arbitrarySpan, 2878 }}, 2879 }, { 2880 Speculative: true, 2881 Anchor: &xpb.Anchor{ 2882 Ticket: "kythe:?path=someFile#someOverrideCallerAnchor2", 2883 Parent: "kythe:?path=someFile", 2884 Span: arbitrarySpan, 2885 }, 2886 Site: []*xpb.Anchor{{ 2887 Ticket: "kythe:?path=someFile#someCallsiteAnchor", 2888 Parent: "kythe:?path=someFile", 2889 }}, 2890 }}, 2891 } 2892 2893 if err := testutil.DeepEqual(&xpb.CrossReferencesReply_Total{ 2894 Callers: 3, 2895 }, reply.Total); err != nil { 2896 t.Error(err) 2897 } 2898 2899 xr := reply.CrossReferences[ticket] 2900 if xr == nil { 2901 t.Fatalf("Missing expected CrossReferences; found: %#v", reply) 2902 } else if err := testutil.DeepEqual(expected, xr); err != nil { 2903 t.Fatal(err) 2904 } 2905 } 2906 2907 func TestCrossReferencesRevisions(t *testing.T) { 2908 ticket := "kythe://someCorpus?lang=otpl#withInfos" 2909 2910 st := tbl.Construct(t) 2911 reply, err := st.CrossReferences(ctx, &xpb.CrossReferencesRequest{ 2912 Ticket: []string{ticket}, 2913 DefinitionKind: xpb.CrossReferencesRequest_ALL_DEFINITIONS, 2914 ReferenceKind: xpb.CrossReferencesRequest_ALL_REFERENCES, 2915 Snippets: xpb.SnippetsKind_NONE, 2916 }) 2917 testutil.Fatalf(t, "CrossReferencesRequest error: %v", err) 2918 2919 expected := &xpb.CrossReferencesReply{ 2920 Total: &xpb.CrossReferencesReply_Total{ 2921 References: 1, 2922 RefEdgeToCount: map[string]int64{ 2923 "/kythe/edge/ref": 1, 2924 }, 2925 }, 2926 Filtered: &xpb.CrossReferencesReply_Total{}, 2927 CrossReferences: map[string]*xpb.CrossReferencesReply_CrossReferenceSet{ 2928 ticket: &xpb.CrossReferencesReply_CrossReferenceSet{ 2929 Ticket: ticket, 2930 2931 Reference: []*xpb.CrossReferencesReply_RelatedAnchor{{ 2932 Anchor: &xpb.Anchor{ 2933 Ticket: "kythe://corpus?lang=otpl?path=some/file#27-33", 2934 Kind: "/kythe/edge/ref", 2935 Parent: "kythe://corpus?path=some/file", 2936 Revision: "someFileRev", // ⟵ this is expected now 2937 2938 Span: &cpb.Span{ 2939 Start: &cpb.Point{ 2940 ByteOffset: 27, 2941 LineNumber: 2, 2942 ColumnOffset: 10, 2943 }, 2944 End: &cpb.Point{ 2945 ByteOffset: 33, 2946 LineNumber: 3, 2947 ColumnOffset: 5, 2948 }, 2949 }, 2950 }, 2951 }}, 2952 }, 2953 }, 2954 } 2955 2956 if diff := compare.ProtoDiff(expected, reply); diff != "" { 2957 t.Fatalf("(-expected; +found):\n%s", diff) 2958 } 2959 } 2960 2961 func nodeInfos(nss ...[]*srvpb.Node) map[string]*cpb.NodeInfo { 2962 m := make(map[string]*cpb.NodeInfo) 2963 for _, ns := range nss { 2964 for _, n := range ns { 2965 if ni := nodeInfo(n); ni != nil { 2966 m[n.Ticket] = ni 2967 } 2968 } 2969 } 2970 return m 2971 } 2972 2973 func TestDocumentationEmpty(t *testing.T) { 2974 st := tbl.Construct(t) 2975 reply, err := st.Documentation(ctx, &xpb.DocumentationRequest{ 2976 Ticket: []string{"kythe:#undocumented"}, 2977 }) 2978 2979 expected := &xpb.DocumentationReply{} 2980 2981 if reply == nil || err != nil { 2982 t.Fatalf("Documentation call failed: (reply: %v; error: %v)", reply, err) 2983 } else if err := testutil.DeepEqual(expected, reply); err != nil { 2984 t.Fatal(err) 2985 } 2986 } 2987 2988 func TestDocumentation(t *testing.T) { 2989 st := tbl.Construct(t) 2990 reply, err := st.Documentation(ctx, &xpb.DocumentationRequest{ 2991 Ticket: []string{"kythe:#documented"}, 2992 }) 2993 2994 expected := &xpb.DocumentationReply{ 2995 Document: []*xpb.DocumentationReply_Document{{ 2996 Ticket: "kythe:#documented", 2997 Text: &xpb.Printable{ 2998 RawText: "some documentation text", 2999 }, 3000 MarkedSource: &cpb.MarkedSource{ 3001 Kind: cpb.MarkedSource_IDENTIFIER, 3002 PreText: "DocumentBuilderFactory", 3003 }, 3004 }}, 3005 Nodes: nodeInfos(getNodes("kythe:#documented")), 3006 DefinitionLocations: map[string]*xpb.Anchor{ 3007 "kythe:?path=def/location#defDoc": &xpb.Anchor{ 3008 Ticket: "kythe:?path=def/location#defDoc", 3009 Parent: "kythe:?path=def/location", 3010 Span: &cpb.Span{ 3011 Start: &cpb.Point{ 3012 ByteOffset: 1, 3013 LineNumber: 1, 3014 ColumnOffset: 1, 3015 }, 3016 End: &cpb.Point{ 3017 ByteOffset: 4, 3018 LineNumber: 1, 3019 ColumnOffset: 4, 3020 }, 3021 }, 3022 }, 3023 }, 3024 } 3025 3026 if reply == nil || err != nil { 3027 t.Fatalf("Documentation call failed: (reply: %v; error: %v)", reply, err) 3028 } else if diff := compare.ProtoDiff(expected, reply); diff != "" { 3029 t.Fatalf("(-expected; +found):\n%s", diff) 3030 } 3031 } 3032 3033 func TestDocumentationChildren(t *testing.T) { 3034 st := tbl.Construct(t) 3035 reply, err := st.Documentation(ctx, &xpb.DocumentationRequest{ 3036 Ticket: []string{"kythe:#documented"}, 3037 3038 IncludeChildren: true, 3039 }) 3040 3041 expected := &xpb.DocumentationReply{ 3042 Document: []*xpb.DocumentationReply_Document{{ 3043 Ticket: "kythe:#documented", 3044 Text: &xpb.Printable{ 3045 RawText: "some documentation text", 3046 }, 3047 MarkedSource: &cpb.MarkedSource{ 3048 Kind: cpb.MarkedSource_IDENTIFIER, 3049 PreText: "DocumentBuilderFactory", 3050 }, 3051 Children: []*xpb.DocumentationReply_Document{{ 3052 Ticket: "kythe:#childDoc", 3053 Text: &xpb.Printable{ 3054 RawText: "child document text", 3055 }, 3056 }, { 3057 Ticket: "kythe:#childDocBy", 3058 Text: &xpb.Printable{ 3059 RawText: "second child document text", 3060 }, 3061 }}, 3062 }}, 3063 Nodes: nodeInfos(getNodes( 3064 "kythe:#childDoc", 3065 "kythe:#childDocBy", 3066 "kythe:#documented", 3067 "kythe:#secondChildDoc", 3068 )), 3069 DefinitionLocations: map[string]*xpb.Anchor{ 3070 "kythe:?path=def/location#defDoc": &xpb.Anchor{ 3071 Ticket: "kythe:?path=def/location#defDoc", 3072 Parent: "kythe:?path=def/location", 3073 Span: &cpb.Span{ 3074 Start: &cpb.Point{ 3075 ByteOffset: 1, 3076 LineNumber: 1, 3077 ColumnOffset: 1, 3078 }, 3079 End: &cpb.Point{ 3080 ByteOffset: 4, 3081 LineNumber: 1, 3082 ColumnOffset: 4, 3083 }, 3084 }, 3085 }, 3086 }, 3087 } 3088 3089 if reply == nil || err != nil { 3090 t.Fatalf("Documentation call failed: (reply: %v; error: %v)", reply, err) 3091 } else if diff := compare.ProtoDiff(expected, reply); diff != "" { 3092 t.Fatalf("(-expected; +found):\n%s", diff) 3093 } 3094 } 3095 3096 func TestDocumentationPatching(t *testing.T) { 3097 st := tbl.Construct(t) 3098 3099 patcher := &mockPatcher{} 3100 st.MakePatcher = func(ctx context.Context, ws *xpb.Workspace) (MultiFilePatcher, error) { 3101 return patcher, nil 3102 } 3103 3104 reply, err := st.Documentation(ctx, &xpb.DocumentationRequest{ 3105 Ticket: []string{"kythe:#documented"}, 3106 3107 IncludeChildren: true, 3108 3109 Workspace: &xpb.Workspace{Uri: "test:"}, 3110 PatchAgainstWorkspace: true, 3111 }) 3112 3113 expected := &xpb.DocumentationReply{ 3114 Document: []*xpb.DocumentationReply_Document{{ 3115 Ticket: "kythe:#documented", 3116 Text: &xpb.Printable{ 3117 RawText: "some documentation text", 3118 }, 3119 MarkedSource: &cpb.MarkedSource{ 3120 Kind: cpb.MarkedSource_IDENTIFIER, 3121 PreText: "DocumentBuilderFactory", 3122 }, 3123 Children: []*xpb.DocumentationReply_Document{{ 3124 Ticket: "kythe:#childDoc", 3125 Text: &xpb.Printable{ 3126 RawText: "child document text", 3127 }, 3128 }, { 3129 Ticket: "kythe:#childDocBy", 3130 Text: &xpb.Printable{ 3131 RawText: "second child document text", 3132 }, 3133 }}, 3134 }}, 3135 Nodes: nodeInfos(getNodes( 3136 "kythe:#childDoc", 3137 "kythe:#childDocBy", 3138 "kythe:#documented", 3139 "kythe:#secondChildDoc", 3140 )), 3141 DefinitionLocations: map[string]*xpb.Anchor{ 3142 "kythe:?path=def/location#defDoc": &xpb.Anchor{ 3143 Ticket: "kythe:?path=def/location#defDoc", 3144 Parent: "kythe:?path=def/location", 3145 Span: &cpb.Span{ 3146 Start: &cpb.Point{ 3147 ByteOffset: 2, 3148 LineNumber: 2, 3149 ColumnOffset: 2, 3150 }, 3151 End: &cpb.Point{ 3152 ByteOffset: 5, 3153 LineNumber: 2, 3154 ColumnOffset: 5, 3155 }, 3156 }, 3157 }, 3158 }, 3159 } 3160 3161 if reply == nil || err != nil { 3162 t.Fatalf("Documentation call failed: (reply: %v; error: %v)", reply, err) 3163 } else if diff := compare.ProtoDiff(expected, reply); diff != "" { 3164 t.Fatalf("(-expected; +found):\n%s", diff) 3165 } 3166 } 3167 3168 func TestDocumentationIndirection(t *testing.T) { 3169 st := tbl.Construct(t) 3170 reply, err := st.Documentation(ctx, &xpb.DocumentationRequest{ 3171 Ticket: []string{"kythe:#documentedBy"}, 3172 }) 3173 3174 expected := &xpb.DocumentationReply{ 3175 Document: []*xpb.DocumentationReply_Document{{ 3176 Ticket: "kythe:#documentedBy", 3177 Text: &xpb.Printable{ 3178 RawText: "some documentation text", 3179 }, 3180 MarkedSource: &cpb.MarkedSource{ 3181 Kind: cpb.MarkedSource_IDENTIFIER, 3182 PreText: "DocumentBuilderFactory", 3183 }, 3184 }}, 3185 Nodes: nodeInfos(getNodes("kythe:#documented", "kythe:#documentedBy")), 3186 DefinitionLocations: map[string]*xpb.Anchor{ 3187 "kythe:?path=def/location#defDoc": &xpb.Anchor{ 3188 Ticket: "kythe:?path=def/location#defDoc", 3189 Parent: "kythe:?path=def/location", 3190 Span: &cpb.Span{ 3191 Start: &cpb.Point{ 3192 ByteOffset: 1, 3193 LineNumber: 1, 3194 ColumnOffset: 1, 3195 }, 3196 End: &cpb.Point{ 3197 ByteOffset: 4, 3198 LineNumber: 1, 3199 ColumnOffset: 4, 3200 }, 3201 }, 3202 }, 3203 }, 3204 } 3205 3206 if reply == nil || err != nil { 3207 t.Fatalf("Documentation call failed: (reply: %v; error: %v)", reply, err) 3208 } else if diff := compare.ProtoDiff(expected, reply); diff != "" { 3209 t.Fatalf("(-expected; +found):\n%s", diff) 3210 } 3211 } 3212 3213 func TestPageSearchIndex(t *testing.T) { 3214 set := &srvpb.PagedCrossReferences{ 3215 PageSearchIndex: &srvpb.PagedCrossReferences_PageSearchIndex{ 3216 ByCorpus: &srvpb.PagedCrossReferences_PageSearchIndex_Postings{ 3217 Index: map[uint32]*srvpb.PagedCrossReferences_PageSearchIndex_Pages{ 3218 tri("kyt"): pages(0), 3219 tri("the"): pages(0, 1, 2), 3220 tri("yth"): pages(0), 3221 tri("oth"): pages(1), 3222 tri("her"): pages(1), 3223 tri("Pag"): pages(math.MaxUint32), // short-hand for all pages 3224 tri("age"): pages(0, 1, 2), 3225 tri("ge_"): pages(1, 2), 3226 }, 3227 }, 3228 ByRoot: &srvpb.PagedCrossReferences_PageSearchIndex_Postings{ 3229 Index: map[uint32]*srvpb.PagedCrossReferences_PageSearchIndex_Pages{ 3230 tri("baz"): pages(1, 2), 3231 tri("aze"): pages(1, 2), 3232 tri("zel"): pages(1, 2), 3233 }, 3234 }, 3235 ByPath: &srvpb.PagedCrossReferences_PageSearchIndex_Postings{ 3236 Index: map[uint32]*srvpb.PagedCrossReferences_PageSearchIndex_Pages{ 3237 tri("kyt"): pages(0), 3238 tri("yth"): pages(0), 3239 tri("the"): pages(0), 3240 tri("he/"): pages(0), 3241 tri("e/g"): pages(0), 3242 tri("/go"): pages(0), 3243 }, 3244 }, 3245 ByResolvedPath: &srvpb.PagedCrossReferences_PageSearchIndex_Postings{ 3246 Index: map[uint32]*srvpb.PagedCrossReferences_PageSearchIndex_Pages{ 3247 tri("the"): pages(0, 2), 3248 tri("he/"): pages(0, 2), 3249 tri("e/b"): pages(0, 2), 3250 tri("/ba"): pages(0, 2), 3251 }, 3252 }, 3253 }, 3254 PageIndex: []*srvpb.PagedCrossReferences_PageIndex{{ 3255 PageKey: "kythePage", 3256 }, { 3257 PageKey: "otherPage_", 3258 }, { 3259 PageKey: "thePage_", 3260 }}, 3261 } 3262 3263 // All pages are represented by nil 3264 var allPages stringset.Set 3265 3266 tests := []struct { 3267 Filter *xpb.CorpusPathFilters 3268 Keys stringset.Set 3269 }{ 3270 {mustParseFilters(`filter: { type: INCLUDE_ONLY corpus: "kyt" }`), stringset.New("kythePage")}, 3271 {mustParseFilters(`filter: { type: INCLUDE_ONLY corpus: "kythe" }`), stringset.New("kythePage")}, 3272 {mustParseFilters(`filter: { type: INCLUDE_ONLY corpus: "kythe|golang" }`), stringset.New("kythePage")}, 3273 {mustParseFilters(`filter: { type: INCLUDE_ONLY corpus: "other" }`), stringset.New("otherPage_")}, 3274 {mustParseFilters(`filter: { type: INCLUDE_ONLY corpus: "kythe|other" }`), stringset.New("kythePage", "otherPage_")}, 3275 {mustParseFilters(`filter: { type: INCLUDE_ONLY corpus: "Page_" }`), stringset.New("thePage_", "otherPage_")}, 3276 {mustParseFilters(`filter: { type: INCLUDE_ONLY corpus: "the" }`), allPages}, 3277 {mustParseFilters(`filter: { type: INCLUDE_ONLY corpus: "Page" }`), allPages}, 3278 3279 // Trigrams are separated by field 3280 {mustParseFilters(`filter: { type: INCLUDE_ONLY corpus: "kythe/go" }`), stringset.New()}, 3281 {mustParseFilters(`filter: { type: INCLUDE_ONLY corpus: "bazel" }`), stringset.New()}, 3282 {mustParseFilters(`filter: { type: INCLUDE_ONLY corpus: "the/ba" }`), stringset.New()}, 3283 {mustParseFilters(`filter: { type: INCLUDE_ONLY root: "the/ba" }`), stringset.New()}, 3284 {mustParseFilters(`filter: { type: INCLUDE_ONLY path: "the/ba" }`), stringset.New()}, 3285 {mustParseFilters(`filter: { type: INCLUDE_ONLY path: "kythe/go" }`), stringset.New("kythePage")}, 3286 {mustParseFilters(`filter: { type: INCLUDE_ONLY path: "/go" }`), stringset.New("kythePage")}, 3287 {mustParseFilters(`filter: { type: INCLUDE_ONLY root: "bazel" }`), stringset.New("otherPage_", "thePage_")}, 3288 {mustParseFilters(`filter: { type: INCLUDE_ONLY resolved_path: "the/ba" }`), stringset.New("kythePage", "thePage_")}, 3289 3290 // Merge filters 3291 {mustParseFilters(`filter: { type: INCLUDE_ONLY corpus: "kythe|other" } filter: { type: INCLUDE_ONLY corpus: "other" }`), stringset.New("otherPage_")}, 3292 {mustParseFilters(`filter: { type: INCLUDE_ONLY resolved_path: "the/ba" } filter: { type: INCLUDE_ONLY root: "bazel" }`), stringset.New("thePage_")}, 3293 3294 // No trigrams; matches everything 3295 {mustParseFilters(`filter: { type: INCLUDE_ONLY corpus: "ne" }`), allPages}, 3296 {mustParseFilters(`filter: { type: INCLUDE_ONLY corpus: "^$" }`), allPages}, 3297 {mustParseFilters(`filter: { type: INCLUDE_ONLY corpus: "\\.s" }`), allPages}, 3298 3299 // Matches nothing 3300 {mustParseFilters(`filter: { type: INCLUDE_ONLY corpus: "nope" }`), stringset.New()}, 3301 {mustParseFilters(`filter: { type: INCLUDE_ONLY corpus: "rip" }`), stringset.New()}, 3302 {mustParseFilters(`filter: { type: INCLUDE_ONLY corpus: "kyther" }`), stringset.New()}, 3303 {mustParseFilters(`filter: { type: INCLUDE_ONLY corpus: "kythe.+google" }`), stringset.New()}, 3304 {mustParseFilters(`filter: { type: INCLUDE_ONLY corpus: "golang" }`), stringset.New()}, 3305 3306 // Exclusion filter are not handled by the search index; they match all pages. 3307 {mustParseFilters(`filter: { type: EXCLUDE corpus: "kythe" }`), allPages}, 3308 } 3309 3310 for i, test := range tests { 3311 t.Run(strconv.Itoa(i), func(t *testing.T) { 3312 t.Logf("CorpusPathFilters: %s", test.Filter) 3313 filter, err := compileCorpusPathFilters(test.Filter, nil) 3314 testutil.Fatalf(t, "compileCorpusPathFilters: %v", err) 3315 found := filter.PageSet(set) 3316 3317 var expected *pageSet 3318 if test.Keys != nil { 3319 expected = &pageSet{KeySet: test.Keys} 3320 } 3321 if diff := compare.ProtoDiff(expected, found); diff != "" { 3322 t.Fatalf("Unexpected diff (-expected; +found):\n%s", diff) 3323 } 3324 }) 3325 } 3326 } 3327 3328 func pages(ps ...uint32) *srvpb.PagedCrossReferences_PageSearchIndex_Pages { 3329 if len(ps) <= 1 { 3330 return &srvpb.PagedCrossReferences_PageSearchIndex_Pages{PageIndex: ps} 3331 } 3332 encoded := make([]uint32, len(ps)) 3333 encoded[0] = ps[0] 3334 for i, n := range ps[1:] { 3335 encoded[i+1] = n - encoded[i] 3336 } 3337 return &srvpb.PagedCrossReferences_PageSearchIndex_Pages{PageIndex: encoded} 3338 } 3339 3340 func TestCorpusPathFilters(t *testing.T) { 3341 tests := []struct { 3342 filters *xpb.CorpusPathFilters 3343 3344 includes, excludes []string 3345 }{ 3346 {mustParseFilters(`filter: { type: INCLUDE_ONLY corpus: "^kythe3" }`), 3347 []string{cps("kythe3", "", ""), cps("kythe3//branch", "", "")}, 3348 []string{cps("other", "", ""), cps("other", "kythe3", "kythe3")}}, 3349 {mustParseFilters(`filter: { type: EXCLUDE root: ".+" }`), 3350 []string{cps("kythe3", "", ""), cps("kythe3//branch", "", ""), cps("oss", "", "any/path")}, 3351 []string{cps("kythe3", "genfiles", ""), cps("other", "bin", "some/path")}}, 3352 {mustParseFilters(`filter: { type: INCLUDE_ONLY corpus: "^kythe3" root: "genfiles" }`), 3353 []string{cps("kythe3", "genfiles", ""), cps("kythe3//branch", "genfiles", "")}, 3354 []string{cps("kythe2", "genfiles", ""), cps("kythe3", "bin", "path"), cps("other", "bin", "some/path")}}, 3355 {mustParseFilters(`filter: { type: INCLUDE_ONLY corpus: "kythe" } filter: { type: INCLUDE_ONLY corpus: "2|3" }`), 3356 []string{cps("kythe3", "genfiles", "path"), cps("kythe2", "", "blah")}, 3357 []string{cps("kythe4", "", ""), cps("kythe", "kythe2", "kythe3")}}, 3358 {mustParseFilters(`filter: { type: EXCLUDE root: ".+" } filter: { type: INCLUDE_ONLY corpus: "^kythe3"}`), 3359 []string{cps("kythe3", "", "any/path"), cps("kythe3//branch", "", "some/path")}, 3360 []string{cps("kythe3", "genfiles", "any/path"), cps("kythe3//branch", "bin", "some/path")}}, 3361 {mustParseFilters(`filter: { type: INCLUDE_ONLY resolved_path: "^kythe3/branch/genfiles/"}`), 3362 []string{cps("kythe3//branch", "genfiles", "any/path"), cps("kythe3//branch", "genfiles/more", "any/path")}, 3363 []string{cps("kythe3", "bin", "any/path"), cps("kythe3", "genfiles", "some/path")}}, 3364 // The filter should only apply when the corpus matches. 3365 {mustParseFilters(`filter: { type: INCLUDE_ONLY corpus: "^kythe3" path: ".*k.*" corpus_specific_filter: true}`), 3366 []string{cps("kythe3", "", "k1.cc"), cps("kythe3//branch", "", "k3.cc"), cps("other", "", "file.cc")}, 3367 []string{cps("kythe3", "", "file.cc")}}, 3368 } 3369 3370 for i, test := range tests { 3371 t.Run(strconv.Itoa(i), func(t *testing.T) { 3372 f, err := compileCorpusPathFilters(test.filters, nil) 3373 testutil.Fatalf(t, "Error: %v", err) 3374 3375 for _, include := range test.includes { 3376 if !f.AllowTicket(include) { 3377 t.Errorf("Expected %q to be included but it wasn't", include) 3378 } 3379 } 3380 for _, exclude := range test.excludes { 3381 if f.AllowTicket(exclude) { 3382 t.Errorf("Expected %q to be excluded but it wasn't", exclude) 3383 } 3384 } 3385 }) 3386 } 3387 } 3388 3389 func randPostings(rand *rand.Rand) *srvpb.PagedCrossReferences_PageSearchIndex_Postings { 3390 numPages := rand.Intn(24) + 1 3391 p := &srvpb.PagedCrossReferences_PageSearchIndex_Postings{ 3392 Index: make(map[uint32]*srvpb.PagedCrossReferences_PageSearchIndex_Pages, numPages), 3393 } 3394 n := rand.Intn(32) + 1 3395 for j := 0; j < n; j++ { 3396 s := randTrigram(rand) 3397 t := tri(s) 3398 ps := make([]uint32, rand.Intn(numPages)) 3399 if len(ps) == 0 { 3400 ps = append([]uint32{}, allPages...) 3401 } else { 3402 for k := 0; k < len(ps); k++ { 3403 ps[k] = uint32(rand.Intn(numPages)) 3404 } 3405 3406 // Sort and dedup the pages 3407 sort.Slice(ps, func(i, j int) bool { return ps[i] < ps[j] }) 3408 var k int 3409 for l := 1; l < len(ps); l++ { 3410 if ps[l-1] == ps[l] { 3411 continue 3412 } 3413 ps[k] = ps[l] 3414 k++ 3415 } 3416 ps = ps[:k] 3417 } 3418 p.Index[t] = pages(ps...) 3419 } 3420 return p 3421 } 3422 3423 func randQuery(rand *rand.Rand) []*index.Query { 3424 qs := make([]*index.Query, rand.Intn(4)+1) 3425 if rand.Intn(1000) == 0 { 3426 return nil 3427 } 3428 for j := 0; j < len(qs); j++ { 3429 q := &index.Query{} 3430 o := rand.Intn(100) 3431 switch { 3432 case o == 0: 3433 q.Op = index.QAll 3434 case o == 99: 3435 q.Op = index.QNone 3436 case o < 50: 3437 q.Op = index.QAnd 3438 default: 3439 q.Op = index.QOr 3440 } 3441 if q.Op == index.QAll || q.Op == index.QOr { 3442 n := rand.Intn(4) + 1 3443 for k := 0; k < n; k++ { 3444 q.Trigram = append(q.Trigram, randTrigram(rand)) 3445 } 3446 } 3447 if rand.Intn(10) == 0 { 3448 q.Sub = randQuery(rand) 3449 } 3450 qs[j] = q 3451 } 3452 return qs 3453 } 3454 3455 func TestApplyQueries(t *testing.T) { 3456 testutil.Fatalf(t, "Error: %v", quick.Check(func(p *srvpb.PagedCrossReferences_PageSearchIndex_Postings, qs []*index.Query) bool { 3457 res := applyQueries(p, qs, nil) 3458 if len(res) > 1 { 3459 for _, x := range res { 3460 // Make sure the result doesn't include the allPages marker. 3461 if x == math.MaxUint32 { 3462 t.Logf("Postings: %s", p) 3463 t.Logf("Query: %s", qs) 3464 t.Logf("Result: %v", res) 3465 return false 3466 } 3467 } 3468 } 3469 return true 3470 }, &quick.Config{Values: func(args []reflect.Value, rand *rand.Rand) { 3471 args[0] = reflect.ValueOf(randPostings(rand)) 3472 args[1] = reflect.ValueOf(randQuery(rand)) 3473 }})) 3474 } 3475 3476 func randTrigram(rand *rand.Rand) string { 3477 return string([]rune{rune(rand.Intn(8)) + 'a', rune(rand.Intn(8)) + 'a', rune(rand.Intn(8)) + 'a'}) 3478 } 3479 3480 func cps(corpus, root, path string) string { 3481 u := &kytheuri.URI{Corpus: corpus, Root: root, Path: path} 3482 return u.String() 3483 } 3484 3485 func mustParseFilters(msg string) *xpb.CorpusPathFilters { 3486 var f xpb.CorpusPathFilters 3487 if err := prototext.Unmarshal([]byte(msg), &f); err != nil { 3488 panic(err) 3489 } 3490 return &f 3491 } 3492 3493 // byOffset implements the sort.Interface for *xpb.CrossReferencesReply_RelatedAnchors. 3494 type byOffset []*xpb.CrossReferencesReply_RelatedAnchor 3495 3496 // Implement the sort.Interface. 3497 func (s byOffset) Len() int { return len(s) } 3498 func (s byOffset) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 3499 func (s byOffset) Less(i, j int) bool { 3500 if s[i].Anchor.Span.Start.ByteOffset != s[j].Anchor.Span.Start.ByteOffset { 3501 return s[i].Anchor.Span.Start.ByteOffset < s[j].Anchor.Span.Start.ByteOffset 3502 } else if len(s[i].Site) == 0 || len(s[i].Site) != len(s[j].Site) { 3503 return len(s[i].Site) < len(s[j].Site) 3504 } 3505 return s[i].Site[0].Span.Start.ByteOffset < s[j].Site[0].Span.Start.ByteOffset 3506 } 3507 3508 func nodeInfo(n *srvpb.Node) *cpb.NodeInfo { 3509 ni := &cpb.NodeInfo{ 3510 Facts: make(map[string][]byte, len(n.Fact)), 3511 Definition: n.DefinitionLocation.GetTicket(), 3512 } 3513 for _, f := range n.Fact { 3514 ni.Facts[f.Name] = f.Value 3515 } 3516 if len(ni.Facts) == 0 && ni.Definition == "" { 3517 return nil 3518 } 3519 return ni 3520 } 3521 3522 func makeFactList(keyVals ...string) []*cpb.Fact { 3523 if len(keyVals)%2 != 0 { 3524 panic("makeFactList: odd number of key values") 3525 } 3526 facts := make([]*cpb.Fact, 0, len(keyVals)/2) 3527 for i := 0; i < len(keyVals); i += 2 { 3528 facts = append(facts, &cpb.Fact{ 3529 Name: keyVals[i], 3530 Value: []byte(keyVals[i+1]), 3531 }) 3532 } 3533 return facts 3534 } 3535 3536 func refs(norm *span.Normalizer, ds []*srvpb.FileDecorations_Decoration, infos []*srvpb.FileInfo) (refs []*xpb.DecorationsReply_Reference) { 3537 fileInfos := makeFileInfoMap(infos) 3538 for _, d := range ds { 3539 r := decorationToReference(norm, d) 3540 r.TargetRevision = fileInfos[r.TargetTicket].GetRevision() 3541 refs = append(refs, r) 3542 } 3543 return 3544 } 3545 3546 type testTable struct { 3547 Nodes []*srvpb.Node 3548 Decorations []*srvpb.FileDecorations 3549 RefSets []*srvpb.PagedCrossReferences 3550 RefPages []*srvpb.PagedCrossReferences_Page 3551 Documents []*srvpb.Document 3552 } 3553 3554 func (tbl *testTable) Construct(t *testing.T) *Table { 3555 p := make(testProtoTable) 3556 for _, d := range tbl.Decorations { 3557 testutil.Fatalf(t, "Error writing file decorations: %v", p.Put(ctx, DecorationsKey(mustFix(t, d.File.Ticket)), d)) 3558 } 3559 for _, cr := range tbl.RefSets { 3560 testutil.Fatalf(t, "Error writing cross-references: %v", p.Put(ctx, CrossReferencesKey(mustFix(t, cr.SourceTicket)), cr)) 3561 } 3562 for _, crp := range tbl.RefPages { 3563 testutil.Fatalf(t, "Error writing cross-references: %v", p.Put(ctx, CrossReferencesPageKey(crp.PageKey), crp)) 3564 } 3565 for _, doc := range tbl.Documents { 3566 testutil.Fatalf(t, "Error writing documents: %v", p.Put(ctx, DocumentationKey(doc.Ticket), doc)) 3567 } 3568 return NewCombinedTable(p) 3569 } 3570 3571 func mustFix(t *testing.T, ticket string) string { 3572 ft, err := kytheuri.Fix(ticket) 3573 if err != nil { 3574 t.Fatalf("Error fixing ticket %q: %v", ticket, err) 3575 } 3576 return ft 3577 } 3578 3579 type testProtoTable map[string]proto.Message 3580 3581 func (t testProtoTable) Put(_ context.Context, key []byte, val proto.Message) error { 3582 t[string(key)] = val 3583 return nil 3584 } 3585 3586 func (t testProtoTable) Lookup(_ context.Context, key []byte, msg proto.Message) error { 3587 m, ok := t[string(key)] 3588 if !ok { 3589 return table.ErrNoSuchKey 3590 } 3591 proto.Merge(msg, m) 3592 return nil 3593 } 3594 3595 func (t testProtoTable) LookupValues(_ context.Context, key []byte, m proto.Message, f func(proto.Message) error) error { 3596 val, ok := t[string(key)] 3597 if !ok { 3598 return nil 3599 } 3600 msg := m.ProtoReflect().New().Interface() 3601 proto.Merge(msg, val) 3602 if err := f(msg); err != nil && err != table.ErrStopLookup { 3603 return err 3604 } 3605 return nil 3606 } 3607 3608 func (t testProtoTable) Buffered() table.BufferedProto { panic("UNIMPLEMENTED") } 3609 3610 func (t testProtoTable) Close(context.Context) error { return nil } 3611 3612 func fi(cp *cpb.CorpusPath, rev string) *srvpb.FileInfo { 3613 return &srvpb.FileInfo{ 3614 CorpusPath: cp, 3615 Revision: rev, 3616 } 3617 } 3618 3619 func cp(corpus, root, path string) *cpb.CorpusPath { 3620 return &cpb.CorpusPath{ 3621 Corpus: corpus, 3622 Root: root, 3623 Path: path, 3624 } 3625 }