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  }