kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/go/serving/graph/columnar_test.go (about) 1 /* 2 * Copyright 2018 The Kythe Authors. All rights reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package graph 18 19 import ( 20 "context" 21 "testing" 22 23 "kythe.io/kythe/go/services/graph" 24 "kythe.io/kythe/go/serving/graph/columnar" 25 "kythe.io/kythe/go/storage/inmemory" 26 "kythe.io/kythe/go/storage/keyvalue" 27 "kythe.io/kythe/go/util/compare" 28 "kythe.io/kythe/go/util/kytheuri" 29 "kythe.io/kythe/go/util/schema/edges" 30 "kythe.io/kythe/go/util/schema/facts" 31 kinds "kythe.io/kythe/go/util/schema/nodes" 32 33 cpb "kythe.io/kythe/proto/common_go_proto" 34 gpb "kythe.io/kythe/proto/graph_go_proto" 35 gspb "kythe.io/kythe/proto/graph_serving_go_proto" 36 scpb "kythe.io/kythe/proto/schema_go_proto" 37 spb "kythe.io/kythe/proto/storage_go_proto" 38 ) 39 40 func mustWriteEdges(t *testing.T, w keyvalue.Writer, e *gspb.Edges) { 41 kv, err := columnar.EncodeEdgesEntry(columnar.EdgesKeyPrefix, e) 42 if err != nil { 43 t.Fatal(err) 44 } 45 mustWrite(t, w, kv.Key, kv.Value) 46 } 47 48 func mustWrite(t *testing.T, w keyvalue.Writer, key, val []byte) { 49 if err := w.Write(key, val); err != nil { 50 t.Fatal(err) 51 } 52 } 53 54 func TestServingEdges(t *testing.T) { 55 ctx := context.Background() 56 db := inmemory.NewKeyValueDB() 57 w, err := db.Writer(ctx) 58 if err != nil { 59 t.Fatal(err) 60 } 61 62 // Mark table as columnar 63 mustWrite(t, w, []byte(ColumnarTableKeyMarker), []byte{}) 64 65 src := &spb.VName{Corpus: "corpus", Signature: "sig"} 66 edgeEntries := []*gspb.Edges{{ 67 Source: src, 68 Entry: &gspb.Edges_Index_{&gspb.Edges_Index{ 69 Node: &scpb.Node{ 70 Kind: &scpb.Node_KytheKind{scpb.NodeKind_RECORD}, 71 Fact: []*scpb.Fact{{ 72 Name: &scpb.Fact_KytheName{scpb.FactName_TEXT}, 73 Value: []byte("value"), 74 }}, 75 }, 76 }}, 77 }, { 78 Source: src, 79 Entry: &gspb.Edges_Edge_{&gspb.Edges_Edge{ 80 Kind: &gspb.Edges_Edge_KytheKind{scpb.EdgeKind_PARAM}, 81 Target: &spb.VName{Signature: "param0"}, 82 }}, 83 }, { 84 Source: src, 85 Entry: &gspb.Edges_Edge_{&gspb.Edges_Edge{ 86 Kind: &gspb.Edges_Edge_KytheKind{scpb.EdgeKind_PARAM}, 87 Ordinal: 1, 88 Target: &spb.VName{Signature: "param1"}, 89 }}, 90 }, { 91 Source: src, 92 Entry: &gspb.Edges_Edge_{&gspb.Edges_Edge{ 93 Kind: &gspb.Edges_Edge_KytheKind{scpb.EdgeKind_CHILD_OF}, 94 Reverse: true, 95 Target: &spb.VName{Signature: "child1"}, 96 }}, 97 }, { 98 Source: src, 99 Entry: &gspb.Edges_Edge_{&gspb.Edges_Edge{ 100 Kind: &gspb.Edges_Edge_KytheKind{scpb.EdgeKind_CHILD_OF}, 101 Reverse: true, 102 Target: &spb.VName{Signature: "child2"}, 103 }}, 104 }, { 105 Source: src, 106 Entry: &gspb.Edges_Target_{&gspb.Edges_Target{ 107 Node: &scpb.Node{ 108 Source: &spb.VName{Signature: "child1"}, 109 Kind: &scpb.Node_KytheKind{scpb.NodeKind_FUNCTION}, 110 }, 111 }}, 112 }, { 113 Source: src, 114 Entry: &gspb.Edges_Target_{&gspb.Edges_Target{ 115 Node: &scpb.Node{ 116 Source: &spb.VName{Signature: "child2"}, 117 Kind: &scpb.Node_KytheKind{scpb.NodeKind_RECORD}, 118 Subkind: &scpb.Node_KytheSubkind{scpb.Subkind_CLASS}, 119 }, 120 }}, 121 }, { 122 Source: src, 123 Entry: &gspb.Edges_Target_{&gspb.Edges_Target{ 124 Node: &scpb.Node{ 125 Source: &spb.VName{Signature: "param0"}, 126 Kind: &scpb.Node_KytheKind{scpb.NodeKind_VARIABLE}, 127 Subkind: &scpb.Node_KytheSubkind{scpb.Subkind_LOCAL_PARAMETER}, 128 }, 129 }}, 130 }} 131 for _, fd := range edgeEntries { 132 mustWriteEdges(t, w, fd) 133 } 134 135 if err := w.Close(); err != nil { 136 t.Fatal(err) 137 } 138 139 gs := NewService(ctx, db) 140 srcTicket := kytheuri.ToString(src) 141 142 t.Run("source_node", makeEdgesTestCase(ctx, gs, &gpb.EdgesRequest{ 143 Ticket: []string{srcTicket}, 144 Kind: []string{"non_existent_kind"}, 145 Filter: []string{"**"}, 146 }, &gpb.EdgesReply{ 147 Nodes: map[string]*cpb.NodeInfo{ 148 srcTicket: &cpb.NodeInfo{ 149 Facts: map[string][]byte{ 150 facts.NodeKind: []byte(kinds.Record), 151 facts.Text: []byte("value"), 152 }, 153 }, 154 }, 155 })) 156 157 t.Run("filter_node_kind", makeEdgesTestCase(ctx, gs, &gpb.EdgesRequest{ 158 Ticket: []string{srcTicket}, 159 Kind: []string{"non_existent_kind"}, 160 Filter: []string{"/kythe/node/kind"}, 161 }, &gpb.EdgesReply{ 162 Nodes: map[string]*cpb.NodeInfo{ 163 srcTicket: { 164 Facts: map[string][]byte{ 165 facts.NodeKind: []byte(kinds.Record), 166 }, 167 }, 168 }, 169 })) 170 171 t.Run("ordinals", makeEdgesTestCase(ctx, gs, &gpb.EdgesRequest{ 172 Ticket: []string{srcTicket}, 173 Kind: []string{edges.Param}, 174 }, &gpb.EdgesReply{ 175 EdgeSets: map[string]*gpb.EdgeSet{ 176 srcTicket: { 177 Groups: map[string]*gpb.EdgeSet_Group{ 178 edges.Param: { 179 Edge: []*gpb.EdgeSet_Group_Edge{{ 180 Ordinal: 0, 181 TargetTicket: "kythe:#param0", 182 }, { 183 Ordinal: 1, 184 TargetTicket: "kythe:#param1", 185 }}, 186 }, 187 }, 188 }, 189 }, 190 })) 191 192 t.Run("reverse", makeEdgesTestCase(ctx, gs, &gpb.EdgesRequest{ 193 Ticket: []string{srcTicket}, 194 Kind: []string{"%" + edges.ChildOf}, 195 }, &gpb.EdgesReply{ 196 EdgeSets: map[string]*gpb.EdgeSet{ 197 srcTicket: { 198 Groups: map[string]*gpb.EdgeSet_Group{ 199 "%" + edges.ChildOf: { 200 Edge: []*gpb.EdgeSet_Group_Edge{{ 201 TargetTicket: "kythe:#child1", 202 }, { 203 TargetTicket: "kythe:#child2", 204 }}, 205 }, 206 }, 207 }, 208 }, 209 })) 210 211 t.Run("filtered_targets", makeEdgesTestCase(ctx, gs, &gpb.EdgesRequest{ 212 Ticket: []string{srcTicket}, 213 Kind: []string{"%" + edges.ChildOf}, 214 Filter: []string{"**"}, 215 }, &gpb.EdgesReply{ 216 EdgeSets: map[string]*gpb.EdgeSet{ 217 srcTicket: { 218 Groups: map[string]*gpb.EdgeSet_Group{ 219 "%" + edges.ChildOf: { 220 Edge: []*gpb.EdgeSet_Group_Edge{{ 221 TargetTicket: "kythe:#child1", 222 }, { 223 TargetTicket: "kythe:#child2", 224 }}, 225 }, 226 }, 227 }, 228 }, 229 Nodes: map[string]*cpb.NodeInfo{ 230 srcTicket: &cpb.NodeInfo{ 231 Facts: map[string][]byte{ 232 facts.NodeKind: []byte(kinds.Record), 233 facts.Text: []byte("value"), 234 }, 235 }, 236 "kythe:#child1": &cpb.NodeInfo{ 237 Facts: map[string][]byte{ 238 facts.NodeKind: []byte(kinds.Function), 239 }, 240 }, 241 "kythe:#child2": &cpb.NodeInfo{ 242 Facts: map[string][]byte{ 243 facts.NodeKind: []byte(kinds.Record), 244 facts.Subkind: []byte(kinds.Class), 245 }, 246 }, 247 }, 248 })) 249 250 t.Run("all", makeEdgesTestCase(ctx, gs, &gpb.EdgesRequest{ 251 Ticket: []string{srcTicket}, 252 Filter: []string{facts.NodeKind}, 253 }, &gpb.EdgesReply{ 254 EdgeSets: map[string]*gpb.EdgeSet{ 255 srcTicket: { 256 Groups: map[string]*gpb.EdgeSet_Group{ 257 edges.Param: { 258 Edge: []*gpb.EdgeSet_Group_Edge{{ 259 Ordinal: 0, 260 TargetTicket: "kythe:#param0", 261 }, { 262 Ordinal: 1, 263 TargetTicket: "kythe:#param1", 264 }}, 265 }, 266 "%" + edges.ChildOf: { 267 Edge: []*gpb.EdgeSet_Group_Edge{{ 268 TargetTicket: "kythe:#child1", 269 }, { 270 TargetTicket: "kythe:#child2", 271 }}, 272 }, 273 }, 274 }, 275 }, 276 Nodes: map[string]*cpb.NodeInfo{ 277 srcTicket: &cpb.NodeInfo{Facts: map[string][]byte{facts.NodeKind: []byte(kinds.Record)}}, 278 "kythe:#child1": &cpb.NodeInfo{Facts: map[string][]byte{facts.NodeKind: []byte(kinds.Function)}}, 279 "kythe:#child2": &cpb.NodeInfo{Facts: map[string][]byte{facts.NodeKind: []byte(kinds.Record)}}, 280 "kythe:#param0": &cpb.NodeInfo{Facts: map[string][]byte{facts.NodeKind: []byte(kinds.Variable)}}, 281 }, 282 })) 283 } 284 285 func TestServingNodes(t *testing.T) { 286 ctx := context.Background() 287 db := inmemory.NewKeyValueDB() 288 w, err := db.Writer(ctx) 289 if err != nil { 290 t.Fatal(err) 291 } 292 293 // Mark table as columnar 294 mustWrite(t, w, []byte(ColumnarTableKeyMarker), []byte{}) 295 296 edgeEntries := []*gspb.Edges{{ 297 Source: &spb.VName{Signature: "node1"}, 298 Entry: &gspb.Edges_Index_{&gspb.Edges_Index{ 299 Node: &scpb.Node{ 300 Kind: &scpb.Node_KytheKind{scpb.NodeKind_RECORD}, 301 Fact: []*scpb.Fact{{ 302 Name: &scpb.Fact_KytheName{scpb.FactName_TEXT}, 303 Value: []byte("value"), 304 }}, 305 }, 306 }}, 307 }, { 308 Source: &spb.VName{Signature: "node2"}, 309 Entry: &gspb.Edges_Index_{&gspb.Edges_Index{ 310 Node: &scpb.Node{ 311 Subkind: &scpb.Node_KytheSubkind{scpb.Subkind_CLASS}, 312 Fact: []*scpb.Fact{{ 313 Name: &scpb.Fact_KytheName{scpb.FactName_TEXT}, 314 Value: []byte("text"), 315 }}, 316 }, 317 }}, 318 }, { 319 Source: &spb.VName{Signature: "node3"}, 320 Entry: &gspb.Edges_Index_{&gspb.Edges_Index{ 321 Node: &scpb.Node{ 322 Kind: &scpb.Node_KytheKind{scpb.NodeKind_VARIABLE}, 323 Subkind: &scpb.Node_KytheSubkind{scpb.Subkind_LOCAL_PARAMETER}, 324 Fact: []*scpb.Fact{{ 325 Name: &scpb.Fact_KytheName{scpb.FactName_TEXT}, 326 Value: []byte("text3"), 327 }}, 328 }, 329 }}, 330 }} 331 for _, fd := range edgeEntries { 332 mustWriteEdges(t, w, fd) 333 } 334 335 if err := w.Close(); err != nil { 336 t.Fatal(err) 337 } 338 339 gs := NewService(ctx, db) 340 341 t.Run("nodes", makeNodesTestCase(ctx, gs, &gpb.NodesRequest{ 342 Ticket: []string{"kythe:#node1", "kythe:#node2", "kythe:#bad", "kythe:#node3"}, 343 }, &gpb.NodesReply{ 344 Nodes: map[string]*cpb.NodeInfo{ 345 "kythe:#node1": &cpb.NodeInfo{ 346 Facts: map[string][]byte{ 347 facts.NodeKind: []byte(kinds.Record), 348 facts.Text: []byte("value"), 349 }, 350 }, 351 "kythe:#node2": &cpb.NodeInfo{ 352 Facts: map[string][]byte{ 353 facts.Subkind: []byte(kinds.Class), 354 facts.Text: []byte("text"), 355 }, 356 }, 357 "kythe:#node3": &cpb.NodeInfo{ 358 Facts: map[string][]byte{ 359 facts.NodeKind: []byte(kinds.Variable), 360 facts.Subkind: []byte(kinds.LocalParameter), 361 facts.Text: []byte("text3"), 362 }, 363 }, 364 }, 365 })) 366 367 t.Run("single_node", makeNodesTestCase(ctx, gs, &gpb.NodesRequest{ 368 Ticket: []string{"kythe:#node1"}, 369 }, &gpb.NodesReply{ 370 Nodes: map[string]*cpb.NodeInfo{ 371 "kythe:#node1": &cpb.NodeInfo{ 372 Facts: map[string][]byte{ 373 facts.NodeKind: []byte(kinds.Record), 374 facts.Text: []byte("value"), 375 }, 376 }, 377 }, 378 })) 379 380 t.Run("filter", makeNodesTestCase(ctx, gs, &gpb.NodesRequest{ 381 Ticket: []string{"kythe:#node1", "kythe:#node2", "kythe:#bad", "kythe:#node3"}, 382 Filter: []string{facts.NodeKind}, 383 }, &gpb.NodesReply{ 384 Nodes: map[string]*cpb.NodeInfo{ 385 "kythe:#node1": &cpb.NodeInfo{ 386 Facts: map[string][]byte{ 387 facts.NodeKind: []byte(kinds.Record), 388 }, 389 }, 390 "kythe:#node3": &cpb.NodeInfo{ 391 Facts: map[string][]byte{ 392 facts.NodeKind: []byte(kinds.Variable), 393 }, 394 }, 395 }, 396 })) 397 } 398 399 func makeEdgesTestCase(ctx context.Context, gs graph.Service, req *gpb.EdgesRequest, expected *gpb.EdgesReply) func(*testing.T) { 400 return func(t *testing.T) { 401 reply, err := gs.Edges(ctx, req) 402 if err != nil { 403 t.Fatalf("Edges error: %v", err) 404 } 405 if diff := compare.ProtoDiff(expected, reply); diff != "" { 406 t.Fatalf("EdgesReply differences: (- expected; + found)\n%s", diff) 407 } 408 } 409 } 410 411 func makeNodesTestCase(ctx context.Context, gs graph.Service, req *gpb.NodesRequest, expected *gpb.NodesReply) func(*testing.T) { 412 return func(t *testing.T) { 413 reply, err := gs.Nodes(ctx, req) 414 if err != nil { 415 t.Fatalf("Nodes error: %v", err) 416 } 417 if diff := compare.ProtoDiff(expected, reply); diff != "" { 418 t.Fatalf("NodesReply differences: (- expected; + found)\n%s", diff) 419 } 420 } 421 }