github.com/gopherd/gonum@v0.0.4/graph/graphs/gen/gen_test.go (about)

     1  // Copyright ©2021 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 gen
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"testing"
    11  
    12  	"github.com/gopherd/gonum/graph"
    13  	"github.com/gopherd/gonum/graph/encoding/dot"
    14  	"github.com/gopherd/gonum/graph/simple"
    15  )
    16  
    17  type nodeIDGraphBuilder interface {
    18  	graph.Graph
    19  	NodeIDGraphBuilder
    20  }
    21  
    22  func undirected() nodeIDGraphBuilder { return simple.NewUndirectedGraph() }
    23  func directed() nodeIDGraphBuilder   { return simple.NewDirectedGraph() }
    24  
    25  type empty struct{}
    26  
    27  func (r empty) Len() int       { return 0 }
    28  func (r empty) ID(i int) int64 { panic("called ID on empty IDer") }
    29  
    30  func panics(fn func()) (panicked bool, msg string) {
    31  	defer func() {
    32  		r := recover()
    33  		if r != nil {
    34  			panicked = true
    35  			msg = fmt.Sprint(r)
    36  		}
    37  	}()
    38  	fn()
    39  	return
    40  }
    41  
    42  func TestComplete(t *testing.T) {
    43  	tests := []struct {
    44  		name   string
    45  		ids    IDer
    46  		dst    func() nodeIDGraphBuilder
    47  		want   string
    48  		panics string
    49  	}{
    50  		{
    51  			name: "empty",
    52  			ids:  empty{},
    53  			dst:  undirected,
    54  			want: `strict graph empty {
    55  }`,
    56  		},
    57  		{
    58  			name: "single",
    59  			ids:  IDRange{First: 1, Last: 1},
    60  			dst:  undirected,
    61  			want: `strict graph single {
    62   // Node definitions.
    63   1;
    64  }`,
    65  		},
    66  		{
    67  			name: "pair_undirected",
    68  			ids:  IDRange{First: 1, Last: 2},
    69  			dst:  undirected,
    70  			want: `strict graph pair_undirected {
    71   // Node definitions.
    72   1;
    73   2;
    74  
    75   // Edge definitions.
    76   1 -- 2;
    77  }`,
    78  		},
    79  		{
    80  			name: "pair_directed",
    81  			ids:  IDRange{First: 1, Last: 2},
    82  			dst:  directed,
    83  			want: `strict digraph pair_directed {
    84   // Node definitions.
    85   1;
    86   2;
    87  
    88   // Edge definitions.
    89   1 -> 2;
    90  }`,
    91  		},
    92  		{
    93  			name: "quad_undirected",
    94  			ids:  IDRange{First: 1, Last: 4},
    95  			dst:  undirected,
    96  			want: `strict graph quad_undirected {
    97   // Node definitions.
    98   1;
    99   2;
   100   3;
   101   4;
   102  
   103   // Edge definitions.
   104   1 -- 2;
   105   1 -- 3;
   106   1 -- 4;
   107   2 -- 3;
   108   2 -- 4;
   109   3 -- 4;
   110  }`,
   111  		},
   112  		{
   113  			name: "quad_directed",
   114  			ids:  IDRange{First: 1, Last: 4},
   115  			dst:  directed,
   116  			want: `strict digraph quad_directed {
   117   // Node definitions.
   118   1;
   119   2;
   120   3;
   121   4;
   122  
   123   // Edge definitions.
   124   1 -> 2;
   125   1 -> 3;
   126   1 -> 4;
   127   2 -> 3;
   128   2 -> 4;
   129   3 -> 4;
   130  }`,
   131  		},
   132  		{
   133  			name:   "collision",
   134  			ids:    IDSet{1, 2, 3, 2},
   135  			dst:    undirected,
   136  			panics: "gen: node ID collision i=1 j=3: id=2",
   137  		},
   138  	}
   139  
   140  	for _, test := range tests {
   141  		dst := test.dst()
   142  		panicked, msg := panics(func() { Complete(dst, test.ids) })
   143  		if msg != test.panics {
   144  			t.Errorf("unexpected panic message for %q: got:%q want:%q", test.name, msg, test.panics)
   145  		}
   146  		if panicked {
   147  			continue
   148  		}
   149  		got, err := dot.Marshal(dst, test.name, "", " ")
   150  		if err != nil {
   151  			t.Errorf("unexpected marshaling graph error: %v", err)
   152  		}
   153  		if !bytes.Equal(got, []byte(test.want)) {
   154  			t.Errorf("unexpected result for test %s:\ngot:\n%s\nwant:\n%s", test.name, got, test.want)
   155  		}
   156  	}
   157  }
   158  
   159  func TestCycle(t *testing.T) {
   160  	tests := []struct {
   161  		name   string
   162  		ids    IDer
   163  		dst    func() nodeIDGraphBuilder
   164  		want   string
   165  		panics string
   166  	}{
   167  		{
   168  			name: "empty",
   169  			ids:  empty{},
   170  			dst:  undirected,
   171  			want: `strict graph empty {
   172  }`,
   173  		},
   174  		{
   175  			name: "single",
   176  			ids:  IDRange{First: 1, Last: 1},
   177  			dst:  undirected,
   178  			want: `strict graph single {
   179   // Node definitions.
   180   1;
   181  }`,
   182  		},
   183  		{
   184  			name: "pair_undirected",
   185  			ids:  IDRange{First: 1, Last: 2},
   186  			dst:  undirected,
   187  			want: `strict graph pair_undirected {
   188   // Node definitions.
   189   1;
   190   2;
   191  
   192   // Edge definitions.
   193   1 -- 2;
   194  }`,
   195  		},
   196  		{
   197  			name: "pair_directed",
   198  			ids:  IDRange{First: 1, Last: 2},
   199  			dst:  directed,
   200  			want: `strict digraph pair_directed {
   201   // Node definitions.
   202   1;
   203   2;
   204  
   205   // Edge definitions.
   206   1 -> 2;
   207   2 -> 1;
   208  }`,
   209  		},
   210  		{
   211  			name: "quad_undirected",
   212  			ids:  IDRange{First: 1, Last: 4},
   213  			dst:  undirected,
   214  			want: `strict graph quad_undirected {
   215   // Node definitions.
   216   1;
   217   2;
   218   3;
   219   4;
   220  
   221   // Edge definitions.
   222   1 -- 2;
   223   1 -- 4;
   224   2 -- 3;
   225   3 -- 4;
   226  }`,
   227  		},
   228  		{
   229  			name: "quad_directed",
   230  			ids:  IDRange{First: 1, Last: 4},
   231  			dst:  directed,
   232  			want: `strict digraph quad_directed {
   233   // Node definitions.
   234   1;
   235   2;
   236   3;
   237   4;
   238  
   239   // Edge definitions.
   240   1 -> 2;
   241   2 -> 3;
   242   3 -> 4;
   243   4 -> 1;
   244  }`,
   245  		},
   246  		{
   247  			name:   "collision",
   248  			ids:    IDSet{1, 2, 3, 2},
   249  			dst:    undirected,
   250  			panics: "gen: node ID collision i=1 j=3: id=2",
   251  		},
   252  	}
   253  
   254  	for _, test := range tests {
   255  		dst := test.dst()
   256  		panicked, msg := panics(func() { Cycle(dst, test.ids) })
   257  		if msg != test.panics {
   258  			t.Errorf("unexpected panic message for %q: got:%q want:%q", test.name, msg, test.panics)
   259  		}
   260  		if panicked {
   261  			continue
   262  		}
   263  		got, err := dot.Marshal(dst, test.name, "", " ")
   264  		if err != nil {
   265  			t.Errorf("unexpected error marshaling graph: %v", err)
   266  		}
   267  		if !bytes.Equal(got, []byte(test.want)) {
   268  			t.Errorf("unexpected result for test %s:\ngot:\n%s\nwant:\n%s", test.name, got, test.want)
   269  		}
   270  	}
   271  }
   272  
   273  func TestPath(t *testing.T) {
   274  	tests := []struct {
   275  		name   string
   276  		ids    IDer
   277  		dst    func() nodeIDGraphBuilder
   278  		want   string
   279  		panics string
   280  	}{
   281  		{
   282  			name: "empty",
   283  			ids:  empty{},
   284  			dst:  undirected,
   285  			want: `strict graph empty {
   286  }`,
   287  		},
   288  		{
   289  			name: "single",
   290  			ids:  IDRange{First: 1, Last: 1},
   291  			dst:  undirected,
   292  			want: `strict graph single {
   293   // Node definitions.
   294   1;
   295  }`,
   296  		},
   297  		{
   298  			name: "pair_undirected",
   299  			ids:  IDRange{First: 1, Last: 2},
   300  			dst:  undirected,
   301  			want: `strict graph pair_undirected {
   302   // Node definitions.
   303   1;
   304   2;
   305  
   306   // Edge definitions.
   307   1 -- 2;
   308  }`,
   309  		},
   310  		{
   311  			name: "pair_directed",
   312  			ids:  IDRange{First: 1, Last: 2},
   313  			dst:  directed,
   314  			want: `strict digraph pair_directed {
   315   // Node definitions.
   316   1;
   317   2;
   318  
   319   // Edge definitions.
   320   1 -> 2;
   321  }`,
   322  		},
   323  		{
   324  			name: "quad_undirected",
   325  			ids:  IDRange{First: 1, Last: 4},
   326  			dst:  undirected,
   327  			want: `strict graph quad_undirected {
   328   // Node definitions.
   329   1;
   330   2;
   331   3;
   332   4;
   333  
   334   // Edge definitions.
   335   1 -- 2;
   336   2 -- 3;
   337   3 -- 4;
   338  }`,
   339  		},
   340  		{
   341  			name: "quad_directed",
   342  			ids:  IDRange{First: 1, Last: 4},
   343  			dst:  directed,
   344  			want: `strict digraph quad_directed {
   345   // Node definitions.
   346   1;
   347   2;
   348   3;
   349   4;
   350  
   351   // Edge definitions.
   352   1 -> 2;
   353   2 -> 3;
   354   3 -> 4;
   355  }`,
   356  		},
   357  		{
   358  			name:   "collision",
   359  			ids:    IDSet{1, 2, 3, 2},
   360  			dst:    undirected,
   361  			panics: "gen: node ID collision i=1 j=3: id=2",
   362  		},
   363  	}
   364  
   365  	for _, test := range tests {
   366  		dst := test.dst()
   367  		panicked, msg := panics(func() { Path(dst, test.ids) })
   368  		if msg != test.panics {
   369  			t.Errorf("unexpected panic message for %q: got:%q want:%q", test.name, msg, test.panics)
   370  		}
   371  		if panicked {
   372  			continue
   373  		}
   374  		got, err := dot.Marshal(dst, test.name, "", " ")
   375  		if err != nil {
   376  			t.Errorf("unexpected error marshaling graph: %v", err)
   377  		}
   378  		if !bytes.Equal(got, []byte(test.want)) {
   379  			t.Errorf("unexpected result for test %s:\ngot:\n%s\nwant:\n%s", test.name, got, test.want)
   380  		}
   381  	}
   382  }
   383  
   384  func TestStar(t *testing.T) {
   385  	tests := []struct {
   386  		name   string
   387  		center int64
   388  		leaves IDer
   389  		dst    func() nodeIDGraphBuilder
   390  		want   string
   391  		panics string
   392  	}{
   393  		{
   394  			name:   "empty_leaves",
   395  			center: 0,
   396  			leaves: empty{},
   397  			dst:    undirected,
   398  			want: `strict graph empty_leaves {
   399   // Node definitions.
   400   0;
   401  }`,
   402  		},
   403  		{
   404  			name:   "single",
   405  			center: 0,
   406  			leaves: IDRange{First: 1, Last: 1},
   407  			dst:    undirected,
   408  			want: `strict graph single {
   409   // Node definitions.
   410   0;
   411   1;
   412  
   413   // Edge definitions.
   414   0 -- 1;
   415  }`,
   416  		},
   417  		{
   418  			name:   "pair_undirected",
   419  			center: 0,
   420  			leaves: IDRange{First: 1, Last: 2},
   421  			dst:    undirected,
   422  			want: `strict graph pair_undirected {
   423   // Node definitions.
   424   0;
   425   1;
   426   2;
   427  
   428   // Edge definitions.
   429   0 -- 1;
   430   0 -- 2;
   431  }`,
   432  		},
   433  		{
   434  			name:   "pair_directed",
   435  			center: 0,
   436  			leaves: IDRange{First: 1, Last: 2},
   437  			dst:    directed,
   438  			want: `strict digraph pair_directed {
   439   // Node definitions.
   440   0;
   441   1;
   442   2;
   443  
   444   // Edge definitions.
   445   0 -> 1;
   446   0 -> 2;
   447  }`,
   448  		},
   449  		{
   450  			name:   "quad_undirected",
   451  			center: 0,
   452  			leaves: IDRange{First: 1, Last: 4},
   453  			dst:    undirected,
   454  			want: `strict graph quad_undirected {
   455   // Node definitions.
   456   0;
   457   1;
   458   2;
   459   3;
   460   4;
   461  
   462   // Edge definitions.
   463   0 -- 1;
   464   0 -- 2;
   465   0 -- 3;
   466   0 -- 4;
   467  }`,
   468  		},
   469  		{
   470  			name:   "quad_directed",
   471  			center: 0,
   472  			leaves: IDRange{First: 1, Last: 4},
   473  			dst:    directed,
   474  			want: `strict digraph quad_directed {
   475   // Node definitions.
   476   0;
   477   1;
   478   2;
   479   3;
   480   4;
   481  
   482   // Edge definitions.
   483   0 -> 1;
   484   0 -> 2;
   485   0 -> 3;
   486   0 -> 4;
   487  }`,
   488  		},
   489  		{
   490  			name:   "center collision",
   491  			center: 1,
   492  			leaves: IDRange{First: 1, Last: 4},
   493  			dst:    undirected,
   494  			panics: "gen: node ID collision i=0 with extra: id=1",
   495  		},
   496  		{
   497  			name:   "leaf collision",
   498  			center: 0,
   499  			leaves: IDSet{1, 2, 3, 2},
   500  			dst:    undirected,
   501  			panics: "gen: node ID collision i=1 j=3: id=2",
   502  		},
   503  	}
   504  
   505  	for _, test := range tests {
   506  		dst := test.dst()
   507  		panicked, msg := panics(func() { Star(dst, test.center, test.leaves) })
   508  		if msg != test.panics {
   509  			t.Errorf("unexpected panic message for %q: got:%q want:%q", test.name, msg, test.panics)
   510  		}
   511  		if panicked {
   512  			continue
   513  		}
   514  		got, err := dot.Marshal(dst, test.name, "", " ")
   515  		if err != nil {
   516  			t.Errorf("unexpected error marshaling graph: %v", err)
   517  		}
   518  		if !bytes.Equal(got, []byte(test.want)) {
   519  			t.Errorf("unexpected result for test %s:\ngot:\n%s\nwant:\n%s", test.name, got, test.want)
   520  		}
   521  	}
   522  }
   523  
   524  func TestWheel(t *testing.T) {
   525  	tests := []struct {
   526  		name   string
   527  		center int64
   528  		cycle  IDer
   529  		dst    func() nodeIDGraphBuilder
   530  		want   string
   531  		panics string
   532  	}{
   533  		{
   534  			name:   "empty_cycle",
   535  			center: 0,
   536  			cycle:  empty{},
   537  			dst:    undirected,
   538  			want: `strict graph empty_cycle {
   539   // Node definitions.
   540   0;
   541  }`,
   542  		},
   543  		{
   544  			name:  "single",
   545  			cycle: IDRange{First: 1, Last: 1},
   546  			dst:   undirected,
   547  			want: `strict graph single {
   548   // Node definitions.
   549   0;
   550   1;
   551  
   552   // Edge definitions.
   553   0 -- 1;
   554  }`,
   555  		},
   556  		{
   557  			name:   "pair_undirected",
   558  			center: 0,
   559  			cycle:  IDRange{First: 1, Last: 2},
   560  			dst:    undirected,
   561  			want: `strict graph pair_undirected {
   562   // Node definitions.
   563   0;
   564   1;
   565   2;
   566  
   567   // Edge definitions.
   568   0 -- 1;
   569   0 -- 2;
   570   1 -- 2;
   571  }`,
   572  		},
   573  		{
   574  			name:   "pair_directed",
   575  			center: 0,
   576  			cycle:  IDRange{First: 1, Last: 2},
   577  			dst:    directed,
   578  			want: `strict digraph pair_directed {
   579   // Node definitions.
   580   0;
   581   1;
   582   2;
   583  
   584   // Edge definitions.
   585   0 -> 1;
   586   0 -> 2;
   587   1 -> 2;
   588   2 -> 1;
   589  }`,
   590  		},
   591  		{
   592  			name:   "quad_undirected",
   593  			center: 0,
   594  			cycle:  IDRange{First: 1, Last: 4},
   595  			dst:    undirected,
   596  			want: `strict graph quad_undirected {
   597   // Node definitions.
   598   0;
   599   1;
   600   2;
   601   3;
   602   4;
   603  
   604   // Edge definitions.
   605   0 -- 1;
   606   0 -- 2;
   607   0 -- 3;
   608   0 -- 4;
   609   1 -- 2;
   610   1 -- 4;
   611   2 -- 3;
   612   3 -- 4;
   613  }`,
   614  		},
   615  		{
   616  			name:   "quad_directed",
   617  			center: 0,
   618  			cycle:  IDRange{First: 1, Last: 4},
   619  			dst:    directed,
   620  			want: `strict digraph quad_directed {
   621   // Node definitions.
   622   0;
   623   1;
   624   2;
   625   3;
   626   4;
   627  
   628   // Edge definitions.
   629   0 -> 1;
   630   0 -> 2;
   631   0 -> 3;
   632   0 -> 4;
   633   1 -> 2;
   634   2 -> 3;
   635   3 -> 4;
   636   4 -> 1;
   637  }`,
   638  		},
   639  		{
   640  			name:   "center collision",
   641  			center: 1,
   642  			cycle:  IDRange{First: 1, Last: 4},
   643  			dst:    undirected,
   644  			panics: "gen: node ID collision i=0 with extra: id=1",
   645  		},
   646  		{
   647  			name:   "cycle collision",
   648  			center: 0,
   649  			cycle:  IDSet{1, 2, 3, 2},
   650  			dst:    undirected,
   651  			panics: "gen: node ID collision i=1 j=3: id=2",
   652  		},
   653  	}
   654  
   655  	for _, test := range tests {
   656  		dst := test.dst()
   657  		panicked, msg := panics(func() { Wheel(dst, test.center, test.cycle) })
   658  		if msg != test.panics {
   659  			t.Errorf("unexpected panic message for %q: got:%q want:%q", test.name, msg, test.panics)
   660  		}
   661  		if panicked {
   662  			continue
   663  		}
   664  		got, err := dot.Marshal(dst, test.name, "", " ")
   665  		if err != nil {
   666  			t.Errorf("unexpected error marshaling graph: %v", err)
   667  		}
   668  		if !bytes.Equal(got, []byte(test.want)) {
   669  			t.Errorf("unexpected result for test %s:\ngot:\n%s\nwant:\n%s", test.name, got, test.want)
   670  		}
   671  	}
   672  }
   673  
   674  func TestCheck(t *testing.T) {
   675  	tests := []struct {
   676  		ids   IDer
   677  		extra []int64
   678  		want  string
   679  	}{
   680  		{
   681  			ids: IDSet{1, 2, 3, 4}, extra: []int64{1},
   682  			want: "gen: node ID collision i=0 with extra: id=1",
   683  		},
   684  		{
   685  			ids: IDSet{1, 2, 3, 4}, extra: []int64{5, 2},
   686  			want: "gen: node ID collision i=1 with extra j=1: id=2",
   687  		},
   688  		{
   689  			ids: IDSet{}, extra: []int64{1, 2, 1},
   690  			want: "gen: extra node ID collision i=0 j=2: id=1",
   691  		},
   692  	}
   693  
   694  	for _, test := range tests {
   695  		msg := fmt.Sprint(check(test.ids, test.extra...))
   696  		if msg != test.want {
   697  			t.Errorf("unexpected check panic for ids=%#v extra=%v: got:%q want:%q",
   698  				test.ids, test.extra, msg, test.want)
   699  		}
   700  	}
   701  }
   702  
   703  func TestTree(t *testing.T) {
   704  	tests := []struct {
   705  		name   string
   706  		dst    func() nodeIDGraphBuilder
   707  		nodes  IDer
   708  		fanout int
   709  		want   string
   710  		panics string
   711  	}{
   712  		{
   713  			name:   "empty_tree",
   714  			dst:    undirected,
   715  			nodes:  empty{},
   716  			fanout: 2,
   717  			want: `strict graph empty_tree {
   718  }`,
   719  		},
   720  		{
   721  			name:   "singleton_tree",
   722  			dst:    undirected,
   723  			nodes:  IDSet{0},
   724  			fanout: 0,
   725  			want: `strict graph singleton_tree {
   726   // Node definitions.
   727   0;
   728  }`,
   729  		},
   730  		{
   731  			name:   "full_binary_tree_undirected",
   732  			dst:    undirected,
   733  			nodes:  IDRange{First: 0, Last: 14},
   734  			fanout: 2,
   735  			want: `strict graph full_binary_tree_undirected {
   736   // Node definitions.
   737   0;
   738   1;
   739   2;
   740   3;
   741   4;
   742   5;
   743   6;
   744   7;
   745   8;
   746   9;
   747   10;
   748   11;
   749   12;
   750   13;
   751   14;
   752  
   753   // Edge definitions.
   754   0 -- 1;
   755   0 -- 2;
   756   1 -- 3;
   757   1 -- 4;
   758   2 -- 5;
   759   2 -- 6;
   760   3 -- 7;
   761   3 -- 8;
   762   4 -- 9;
   763   4 -- 10;
   764   5 -- 11;
   765   5 -- 12;
   766   6 -- 13;
   767   6 -- 14;
   768  }`,
   769  		},
   770  		{
   771  			name:   "partial_ternary_tree_undirected",
   772  			dst:    undirected,
   773  			nodes:  IDRange{First: 0, Last: 17},
   774  			fanout: 3,
   775  			want: `strict graph partial_ternary_tree_undirected {
   776   // Node definitions.
   777   0;
   778   1;
   779   2;
   780   3;
   781   4;
   782   5;
   783   6;
   784   7;
   785   8;
   786   9;
   787   10;
   788   11;
   789   12;
   790   13;
   791   14;
   792   15;
   793   16;
   794   17;
   795  
   796   // Edge definitions.
   797   0 -- 1;
   798   0 -- 2;
   799   0 -- 3;
   800   1 -- 4;
   801   1 -- 5;
   802   1 -- 6;
   803   2 -- 7;
   804   2 -- 8;
   805   2 -- 9;
   806   3 -- 10;
   807   3 -- 11;
   808   3 -- 12;
   809   4 -- 13;
   810   4 -- 14;
   811   4 -- 15;
   812   5 -- 16;
   813   5 -- 17;
   814  }`,
   815  		},
   816  		{
   817  			name:   "linear_graph_undirected",
   818  			dst:    undirected,
   819  			nodes:  IDRange{First: 0, Last: 4},
   820  			fanout: 1,
   821  			want: `strict graph linear_graph_undirected {
   822   // Node definitions.
   823   0;
   824   1;
   825   2;
   826   3;
   827   4;
   828  
   829   // Edge definitions.
   830   0 -- 1;
   831   1 -- 2;
   832   2 -- 3;
   833   3 -- 4;
   834  }`,
   835  		},
   836  		{
   837  			name:   "full_ternary_tree_directed",
   838  			dst:    directed,
   839  			nodes:  IDRange{First: 0, Last: 12},
   840  			fanout: 3,
   841  			want: `strict digraph full_ternary_tree_directed {
   842   // Node definitions.
   843   0;
   844   1;
   845   2;
   846   3;
   847   4;
   848   5;
   849   6;
   850   7;
   851   8;
   852   9;
   853   10;
   854   11;
   855   12;
   856  
   857   // Edge definitions.
   858   0 -> 1;
   859   0 -> 2;
   860   0 -> 3;
   861   1 -> 4;
   862   1 -> 5;
   863   1 -> 6;
   864   2 -> 7;
   865   2 -> 8;
   866   2 -> 9;
   867   3 -> 10;
   868   3 -> 11;
   869   3 -> 12;
   870  }`,
   871  		},
   872  		{
   873  			name:   "bad_fanout",
   874  			dst:    undirected,
   875  			nodes:  IDSet{0, 1, 2, 3},
   876  			fanout: -1,
   877  			panics: "gen: invalid fan-out",
   878  		},
   879  		{
   880  			name:   "collision",
   881  			dst:    undirected,
   882  			nodes:  IDSet{0, 1, 2, 3, 2},
   883  			fanout: 2,
   884  			panics: "gen: node ID collision i=2 j=4: id=2",
   885  		},
   886  	}
   887  	for _, test := range tests {
   888  		dst := test.dst()
   889  		panicked, msg := panics(func() { Tree(dst, test.fanout, test.nodes) })
   890  		if msg != test.panics {
   891  			t.Errorf("unexpected panic message for %q: got:%q want:%q", test.name, msg, test.panics)
   892  		}
   893  		if panicked {
   894  			continue
   895  		}
   896  		got, err := dot.Marshal(dst, test.name, "", " ")
   897  		if err != nil {
   898  			t.Errorf("unexpected error marshaling graph: %v", err)
   899  		}
   900  		if !bytes.Equal(got, []byte(test.want)) {
   901  			t.Errorf("unexpected result for test %s:\ngot:\n%s\nwant:\n%s", test.name, got, test.want)
   902  		}
   903  	}
   904  }