gonum.org/v1/gonum@v0.14.0/graph/formats/rdf/graph_test.go (about) 1 // Copyright ©2022 The Gonum Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package rdf_test 6 7 import ( 8 "io" 9 "math" 10 "sort" 11 "strings" 12 "testing" 13 14 "golang.org/x/exp/rand" 15 16 "github.com/google/go-cmp/cmp" 17 18 "gonum.org/v1/gonum/graph" 19 "gonum.org/v1/gonum/graph/formats/rdf" 20 "gonum.org/v1/gonum/graph/internal/set" 21 "gonum.org/v1/gonum/graph/iterator" 22 "gonum.org/v1/gonum/graph/multi" 23 "gonum.org/v1/gonum/graph/testgraph" 24 ) 25 26 func graphBuilder(nodes []graph.Node, edges []testgraph.WeightedLine, _, _ float64) (g graph.Graph, n []graph.Node, e []testgraph.Edge, s, a float64, ok bool) { 27 seen := set.NewNodes() 28 gg := rdf.NewGraph() 29 for _, n := range nodes { 30 seen.Add(n) 31 gg.AddNode(n) 32 } 33 for _, edge := range edges { 34 f := gg.Node(edge.From().ID()) 35 if f == nil { 36 f = edge.From() 37 } 38 t := gg.Node(edge.To().ID()) 39 if t == nil { 40 t = edge.To() 41 } 42 cl := multi.Line{F: f, T: t, UID: edge.ID()} 43 seen.Add(cl.F) 44 seen.Add(cl.T) 45 e = append(e, cl) 46 gg.SetLine(cl) 47 } 48 if len(seen) != 0 { 49 n = make([]graph.Node, 0, len(seen)) 50 } 51 for _, sn := range seen { 52 n = append(n, sn) 53 } 54 return gg, n, e, math.NaN(), math.NaN(), true 55 } 56 57 const ( 58 usesEmpty = true 59 reversesEdges = false // Not tested since Graph is directed, but included for documentation. 60 ) 61 62 func TestGraph(t *testing.T) { 63 t.Run("EdgeExistence", func(t *testing.T) { 64 testgraph.EdgeExistence(t, graphBuilder, reversesEdges) 65 }) 66 t.Run("LineExistence", func(t *testing.T) { 67 testgraph.LineExistence(t, graphBuilder, usesEmpty, reversesEdges) 68 }) 69 t.Run("NodeExistence", func(t *testing.T) { 70 testgraph.NodeExistence(t, graphBuilder) 71 }) 72 t.Run("ReturnAdjacentNodes", func(t *testing.T) { 73 testgraph.ReturnAdjacentNodes(t, graphBuilder, usesEmpty, reversesEdges) 74 }) 75 t.Run("ReturnAllLines", func(t *testing.T) { 76 testgraph.ReturnAllLines(t, graphBuilder, usesEmpty) 77 }) 78 t.Run("ReturnAllNodes", func(t *testing.T) { 79 testgraph.ReturnAllNodes(t, graphBuilder, usesEmpty) 80 }) 81 t.Run("ReturnNodeSlice", func(t *testing.T) { 82 testgraph.ReturnNodeSlice(t, graphBuilder, usesEmpty) 83 }) 84 85 t.Run("RemoveNodes", func(t *testing.T) { 86 g := multi.NewDirectedGraph() 87 it := testgraph.NewRandomNodes(100, 1, func(id int64) graph.Node { return multi.Node(id) }) 88 for it.Next() { 89 g.AddNode(it.Node()) 90 } 91 it.Reset() 92 rnd := rand.New(rand.NewSource(1)) 93 for it.Next() { 94 u := it.Node() 95 d := rnd.Intn(5) 96 vit := g.Nodes() 97 for d >= 0 && vit.Next() { 98 v := vit.Node() 99 d-- 100 g.SetLine(g.NewLine(u, v)) 101 } 102 } 103 testgraph.RemoveNodes(t, g) 104 }) 105 t.Run("AddLines", func(t *testing.T) { 106 testgraph.AddLines(t, 100, 107 multi.NewDirectedGraph(), 108 func(id int64) graph.Node { return multi.Node(id) }, 109 true, // Can update nodes. 110 ) 111 }) 112 t.Run("RemoveLines", func(t *testing.T) { 113 g := multi.NewDirectedGraph() 114 it := testgraph.NewRandomNodes(100, 1, func(id int64) graph.Node { return multi.Node(id) }) 115 for it.Next() { 116 g.AddNode(it.Node()) 117 } 118 it.Reset() 119 var lines []graph.Line 120 rnd := rand.New(rand.NewSource(1)) 121 for it.Next() { 122 u := it.Node() 123 d := rnd.Intn(5) 124 vit := g.Nodes() 125 for d >= 0 && vit.Next() { 126 v := vit.Node() 127 d-- 128 l := g.NewLine(u, v) 129 g.SetLine(l) 130 lines = append(lines, l) 131 } 132 } 133 rnd.Shuffle(len(lines), func(i, j int) { 134 lines[i], lines[j] = lines[j], lines[i] 135 }) 136 testgraph.RemoveLines(t, g, iterator.NewOrderedLines(lines)) 137 }) 138 } 139 140 var removeStatementTests = []struct { 141 name string 142 triples string 143 remove int 144 want string 145 }{ 146 { 147 name: "triangle", 148 triples: ` 149 <ex:a> <ex:p> <ex:b> . 150 <ex:b> <ex:p> <ex:c> . 151 <ex:c> <ex:p> <ex:a> . 152 `, 153 remove: 0, 154 want: ` 155 <ex:b> <ex:p> <ex:c> . 156 <ex:c> <ex:p> <ex:a> . 157 `, 158 }, 159 { 160 name: "star", 161 triples: ` 162 <ex:a> <ex:p> <ex:b> . 163 <ex:a> <ex:p> <ex:c> . 164 <ex:a> <ex:p> <ex:d> . 165 `, 166 remove: 0, 167 want: ` 168 <ex:a> <ex:p> <ex:c> . 169 <ex:a> <ex:p> <ex:d> . 170 `, 171 }, 172 { 173 name: "loop", 174 triples: ` 175 <ex:a> <ex:p> <ex:b> . 176 <ex:b> <ex:p> <ex:a> . 177 `, 178 remove: 0, 179 want: ` 180 <ex:b> <ex:p> <ex:a> . 181 `, 182 }, 183 { 184 name: "parallel", 185 triples: ` 186 <ex:a> <ex:p> <ex:b> . 187 <ex:a> <ex:q> <ex:b> . 188 `, 189 remove: 0, 190 want: ` 191 <ex:a> <ex:q> <ex:b> . 192 `, 193 }, 194 { 195 name: "dumbell", 196 triples: ` 197 <ex:a> <ex:p> <ex:b> . 198 <ex:b> <ex:p> <ex:c> . 199 <ex:c> <ex:p> <ex:a> . 200 201 <ex:a> <ex:p> <ex:d> . 202 203 <ex:d> <ex:p> <ex:e> . 204 <ex:e> <ex:p> <ex:f> . 205 <ex:f> <ex:p> <ex:d> . 206 `, 207 remove: 3, 208 want: ` 209 <ex:a> <ex:p> <ex:b> . 210 <ex:b> <ex:p> <ex:c> . 211 <ex:c> <ex:p> <ex:a> . 212 <ex:d> <ex:p> <ex:e> . 213 <ex:e> <ex:p> <ex:f> . 214 <ex:f> <ex:p> <ex:d> . 215 `, 216 }, 217 } 218 219 func TestRemoveStatement(t *testing.T) { 220 for _, test := range removeStatementTests { 221 g, statements, err := graphFromReader(strings.NewReader(test.triples)) 222 if err != nil { 223 t.Errorf("unexpected error for %q: %v", test.name, err) 224 } 225 226 g.RemoveStatement(statements[test.remove]) 227 228 var gotStatements []string 229 it := g.AllStatements() 230 for it.Next() { 231 gotStatements = append(gotStatements, it.Statement().String()) 232 } 233 sort.Strings(gotStatements) 234 235 got := strings.TrimSpace(strings.Join(gotStatements, "\n")) 236 want := strings.TrimSpace(test.want) 237 238 if got != want { 239 t.Errorf("unexpected result for %q:\n%s", test.name, cmp.Diff(got, want)) 240 } 241 } 242 } 243 244 var removeTermTests = []struct { 245 name string 246 triples string 247 remove string 248 want string 249 }{ 250 { 251 name: "triangle", 252 triples: ` 253 <ex:a> <ex:p> <ex:b> . 254 <ex:b> <ex:p> <ex:c> . 255 <ex:c> <ex:p> <ex:a> . 256 `, 257 remove: "<ex:a>", 258 want: ` 259 <ex:b> <ex:p> <ex:c> . 260 `, 261 }, 262 { 263 name: "star", 264 triples: ` 265 <ex:a> <ex:p> <ex:b> . 266 <ex:a> <ex:p> <ex:c> . 267 <ex:a> <ex:p> <ex:d> . 268 `, 269 remove: "<ex:a>", 270 want: "", 271 }, 272 { 273 name: "loop", 274 triples: ` 275 <ex:a> <ex:p> <ex:b> . 276 <ex:b> <ex:p> <ex:a> . 277 `, 278 remove: "<ex:a>", 279 want: "", 280 }, 281 { 282 name: "parallel", 283 triples: ` 284 <ex:a> <ex:p> <ex:b> . 285 <ex:a> <ex:q> <ex:b> . 286 `, 287 remove: "<ex:a>", 288 want: "", 289 }, 290 { 291 name: "dumbell_1", 292 triples: ` 293 <ex:a> <ex:p> <ex:b> . 294 <ex:b> <ex:p> <ex:c> . 295 <ex:c> <ex:p> <ex:a> . 296 297 <ex:a> <ex:q> <ex:d> . 298 299 <ex:d> <ex:p> <ex:e> . 300 <ex:e> <ex:p> <ex:f> . 301 <ex:f> <ex:p> <ex:d> . 302 `, 303 remove: "<ex:q>", 304 want: ` 305 <ex:a> <ex:p> <ex:b> . 306 <ex:b> <ex:p> <ex:c> . 307 <ex:c> <ex:p> <ex:a> . 308 <ex:d> <ex:p> <ex:e> . 309 <ex:e> <ex:p> <ex:f> . 310 <ex:f> <ex:p> <ex:d> . 311 `, 312 }, 313 { 314 name: "dumbell_2", 315 triples: ` 316 <ex:a> <ex:p> <ex:b> . 317 <ex:b> <ex:p> <ex:c> . 318 <ex:c> <ex:p> <ex:a> . 319 320 <ex:a> <ex:q> <ex:d> . 321 322 <ex:d> <ex:p> <ex:e> . 323 <ex:e> <ex:p> <ex:f> . 324 <ex:f> <ex:p> <ex:d> . 325 `, 326 remove: "<ex:p>", 327 want: ` 328 <ex:a> <ex:q> <ex:d> . 329 `, 330 }, 331 } 332 333 func TestRemoveTerm(t *testing.T) { 334 for _, test := range removeTermTests { 335 g, _, err := graphFromReader(strings.NewReader(test.triples)) 336 if err != nil { 337 t.Errorf("unexpected error for %q: %v", test.name, err) 338 } 339 340 term, ok := g.TermFor(test.remove) 341 if !ok { 342 t.Errorf("couldn't find expected term for %q", test.name) 343 continue 344 } 345 g.RemoveTerm(term) 346 347 var gotStatements []string 348 it := g.AllStatements() 349 for it.Next() { 350 gotStatements = append(gotStatements, it.Statement().String()) 351 } 352 sort.Strings(gotStatements) 353 354 got := strings.TrimSpace(strings.Join(gotStatements, "\n")) 355 want := strings.TrimSpace(test.want) 356 357 if got != want { 358 t.Errorf("unexpected result for %q:\n%s", test.name, cmp.Diff(got, want)) 359 } 360 } 361 } 362 363 func graphFromReader(r io.Reader) (*rdf.Graph, []*rdf.Statement, error) { 364 g := rdf.NewGraph() 365 var statements []*rdf.Statement 366 dec := rdf.NewDecoder(r) 367 for { 368 s, err := dec.Unmarshal() 369 if err != nil { 370 if err == io.EOF { 371 break 372 } 373 return nil, nil, err 374 } 375 statements = append(statements, s) 376 g.AddStatement(s) 377 } 378 return g, statements, nil 379 }