gonum.org/v1/gonum@v0.14.0/graph/formats/cytoscapejs/cytoscapejs_test.go (about)

     1  // Copyright ©2018 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 cytoscapejs
     6  
     7  import (
     8  	"encoding/json"
     9  	"os"
    10  	"path/filepath"
    11  	"reflect"
    12  	"testing"
    13  )
    14  
    15  var cytoscapejsElementsTests = []struct {
    16  	path           string
    17  	wantNodes      int
    18  	wantEdges      int
    19  	wantGraph      []Element
    20  	wantAttributes []string
    21  }{
    22  	{
    23  		path:      "edge-type.json",
    24  		wantNodes: 10,
    25  		wantEdges: 10,
    26  		wantGraph: []Element{
    27  			{Data: ElemData{ID: "n01", Attributes: map[string]interface{}{"type": "bezier"}}},
    28  			{Data: ElemData{ID: "n02"}},
    29  			{Data: ElemData{ID: "e01", Source: "n01", Target: "n02"}, Classes: "bezier"},
    30  			{Data: ElemData{ID: "e02", Source: "n01", Target: "n02"}, Classes: "bezier"},
    31  			{Data: ElemData{ID: "e03", Source: "n02", Target: "n01"}, Classes: "bezier"},
    32  			{Data: ElemData{ID: "n03", Attributes: map[string]interface{}{"type": "unbundled-bezier"}}},
    33  			{Data: ElemData{ID: "n04"}},
    34  			{Data: ElemData{ID: "e04", Source: "n03", Target: "n04"}, Classes: "unbundled-bezier"},
    35  			{Data: ElemData{ID: "n05", Attributes: map[string]interface{}{"type": "unbundled-bezier(multiple)"}}},
    36  			{Data: ElemData{ID: "n06"}},
    37  			{Data: ElemData{ID: "e05", Source: "n05", Target: "n06", Parent: ""}, Classes: "multi-unbundled-bezier"},
    38  			{Data: ElemData{ID: "n07", Attributes: map[string]interface{}{"type": "haystack"}}},
    39  			{Data: ElemData{ID: "n08"}},
    40  			{Data: ElemData{ID: "e06", Source: "n08", Target: "n07"}, Classes: "haystack"},
    41  			{Data: ElemData{ID: "e07", Source: "n08", Target: "n07"}, Classes: "haystack"},
    42  			{Data: ElemData{ID: "e08", Source: "n08", Target: "n07"}, Classes: "haystack"},
    43  			{Data: ElemData{ID: "e09", Source: "n08", Target: "n07"}, Classes: "haystack"},
    44  			{Data: ElemData{ID: "n09", Attributes: map[string]interface{}{"type": "segments"}}},
    45  			{Data: ElemData{ID: "n10"}},
    46  			{Data: ElemData{ID: "e10", Source: "n09", Target: "n10"}, Classes: "segments"},
    47  		},
    48  	},
    49  }
    50  
    51  func TestUnmarshalElements(t *testing.T) {
    52  	for _, test := range cytoscapejsElementsTests {
    53  		data, err := os.ReadFile(filepath.Join("testdata", test.path))
    54  		if err != nil {
    55  			t.Errorf("failed to read %q: %v", test.path, err)
    56  			continue
    57  		}
    58  		var got []Element
    59  		err = json.Unmarshal(data, &got)
    60  		if err != nil {
    61  			t.Errorf("failed to unmarshal %q: %v", test.path, err)
    62  			continue
    63  		}
    64  		var gotNodes, gotEdges int
    65  		for _, e := range got {
    66  			typ, err := e.Type()
    67  			if err != nil {
    68  				t.Errorf("unexpected error finding element type for %+v: %v", e, err)
    69  			}
    70  			switch typ {
    71  			case NodeElement:
    72  				gotNodes++
    73  			case EdgeElement:
    74  				gotEdges++
    75  			}
    76  		}
    77  
    78  		if gotNodes != test.wantNodes {
    79  			t.Errorf("unexpected result for order of %q: got:%d want:%d", test.path, gotNodes, test.wantNodes)
    80  		}
    81  		if gotEdges != test.wantEdges {
    82  			t.Errorf("unexpected result for size of %q: got:%d want:%d", test.path, gotEdges, test.wantEdges)
    83  		}
    84  		if test.wantGraph != nil && !reflect.DeepEqual(got, test.wantGraph) {
    85  			t.Errorf("unexpected result for %q:\ngot:\n%#v\nwant:\n%#v", test.path, got, test.wantGraph)
    86  		}
    87  	}
    88  }
    89  
    90  func TestMarshalElements(t *testing.T) {
    91  	for _, test := range cytoscapejsElementsTests {
    92  		data, err := os.ReadFile(filepath.Join("testdata", test.path))
    93  		if err != nil {
    94  			t.Errorf("failed to read %q: %v", test.path, err)
    95  			continue
    96  		}
    97  		var want []Element
    98  		err = json.Unmarshal(data, &want)
    99  		if err != nil {
   100  			t.Errorf("failed to unmarshal %q: %v", test.path, err)
   101  			continue
   102  		}
   103  		marshaled, err := json.Marshal(want)
   104  		if err != nil {
   105  			t.Errorf("failed to unmarshal %q: %v", test.path, err)
   106  			continue
   107  		}
   108  		var got []Element
   109  		err = json.Unmarshal(marshaled, &got)
   110  		if err != nil {
   111  			t.Errorf("failed to unmarshal %q: %v", test.path, err)
   112  			continue
   113  		}
   114  		if !reflect.DeepEqual(got, want) {
   115  			t.Errorf("unexpected result for %q:\ngot:\n%#v\nwant:\n%#v", test.path, got, want)
   116  		}
   117  	}
   118  }
   119  
   120  var cytoscapejsNodeEdgeTests = []struct {
   121  	path               string
   122  	wantNodes          int
   123  	wantEdges          int
   124  	wantGraph          *Elements
   125  	firstNode          Node
   126  	firstEdge          Edge
   127  	wantNodeAttributes map[string]bool
   128  	wantEdgeAttributes map[string]bool
   129  }{
   130  	{
   131  		path:      "cola-compound.json",
   132  		wantNodes: 9,
   133  		wantEdges: 7,
   134  		wantGraph: &Elements{
   135  			Nodes: []Node{
   136  				{Data: NodeData{ID: "compound-1", Parent: ""}},
   137  				{Data: NodeData{ID: "compound-2", Parent: ""}},
   138  				{Data: NodeData{ID: "compound-3", Parent: ""}},
   139  				{Data: NodeData{ID: "b", Parent: "compound-1"}},
   140  				{Data: NodeData{ID: "c", Parent: "compound-1"}},
   141  				{Data: NodeData{ID: "a", Parent: "compound-2"}},
   142  				{Data: NodeData{ID: "d", Parent: "compound-3"}},
   143  				{Data: NodeData{ID: "e", Parent: "compound-3"}},
   144  				{Data: NodeData{ID: "f", Parent: ""}},
   145  			},
   146  			Edges: []Edge{
   147  				{Data: EdgeData{ID: "ab", Source: "a", Target: "b"}},
   148  				{Data: EdgeData{ID: "bc", Source: "b", Target: "c"}},
   149  				{Data: EdgeData{ID: "ac", Source: "a", Target: "c"}},
   150  				{Data: EdgeData{ID: "cd", Source: "c", Target: "d"}},
   151  				{Data: EdgeData{ID: "de", Source: "d", Target: "e"}},
   152  				{Data: EdgeData{ID: "df", Source: "d", Target: "f"}},
   153  				{Data: EdgeData{ID: "af", Source: "a", Target: "f"}},
   154  			},
   155  		},
   156  	},
   157  	{
   158  		path:      "tokyo-railways.json",
   159  		wantNodes: 943,
   160  		wantEdges: 860,
   161  		firstNode: Node{
   162  			Data: NodeData{
   163  				ID: "8220",
   164  				Attributes: map[string]interface{}{
   165  					"station_name": "京成高砂",
   166  					"close_ymd":    "",
   167  					"lon":          139.866875,
   168  					"post":         "",
   169  					"e_status":     0.0,
   170  					"SUID":         8220.0,
   171  					"station_g_cd": 2300110.0,
   172  					"add":          "東京都葛飾区高砂五丁目28-1",
   173  					"line_cd":      99340.0,
   174  					"selected":     false,
   175  					"open_ymd":     "",
   176  					"name":         "9934001",
   177  					"pref_name":    "東京都",
   178  					"shared_name":  "9934001",
   179  					"lat":          35.750932,
   180  					"x":            1398668.75,
   181  					"y":            -357509.32,
   182  				},
   183  			},
   184  			Position: &Position{
   185  				X: 1398668.75,
   186  				Y: -357509.32,
   187  			},
   188  		},
   189  		firstEdge: Edge{
   190  			Data: EdgeData{
   191  				ID:     "18417",
   192  				Source: "8220",
   193  				Target: "8221",
   194  				Attributes: map[string]interface{}{
   195  					"line_name_k":        "ホクソウテツドウホクソウセン",
   196  					"is_bullet":          false,
   197  					"lon":                140.03784499075186,
   198  					"company_name_k":     "ホクソウテツドウ",
   199  					"zoom":               11.0,
   200  					"SUID":               18417.0,
   201  					"company_type":       0.0,
   202  					"company_name_h":     "北総鉄道株式会社",
   203  					"interaction":        "99340",
   204  					"shared_interaction": "99340",
   205  					"company_url":        "http://www.hokuso-railway.co.jp/",
   206  					"line_name":          "北総鉄道北総線",
   207  					"selected":           false,
   208  					"company_name":       "北総鉄道",
   209  					"company_cd":         152.0,
   210  					"name":               "9934001 (99340) 9934002",
   211  					"rr_cd":              99.0,
   212  					"company_name_r":     "北総鉄道",
   213  					"e_status_x":         0.0,
   214  					"shared_name":        "9934001 (99340) 9934002",
   215  					"lat":                35.78346285846615,
   216  					"e_status_y":         0.0,
   217  					"line_name_h":        "北総鉄道北総線",
   218  				},
   219  			},
   220  		},
   221  		wantNodeAttributes: map[string]bool{
   222  			"station_name": true,
   223  			"close_ymd":    true,
   224  			"lon":          true,
   225  			"post":         true,
   226  			"e_status":     true,
   227  			"SUID":         true,
   228  			"station_g_cd": true,
   229  			"add":          true,
   230  			"line_cd":      true,
   231  			"selected":     true,
   232  			"open_ymd":     true,
   233  			"name":         true,
   234  			"pref_name":    true,
   235  			"shared_name":  true,
   236  			"lat":          true,
   237  			"x":            true,
   238  			"y":            true,
   239  		},
   240  		wantEdgeAttributes: map[string]bool{
   241  			"line_name_k":        true,
   242  			"is_bullet":          true,
   243  			"lon":                true,
   244  			"company_name_k":     true,
   245  			"zoom":               true,
   246  			"SUID":               true,
   247  			"company_type":       true,
   248  			"company_name_h":     true,
   249  			"interaction":        true,
   250  			"shared_interaction": true,
   251  			"company_url":        true,
   252  			"line_name":          true,
   253  			"selected":           true,
   254  			"company_name":       true,
   255  			"company_cd":         true,
   256  			"name":               true,
   257  			"rr_cd":              true,
   258  			"company_name_r":     true,
   259  			"e_status_x":         true,
   260  			"shared_name":        true,
   261  			"lat":                true,
   262  			"e_status_y":         true,
   263  			"line_name_h":        true,
   264  		},
   265  	},
   266  }
   267  
   268  func TestUnmarshalNodeEdge(t *testing.T) {
   269  	for _, test := range cytoscapejsNodeEdgeTests {
   270  		data, err := os.ReadFile(filepath.Join("testdata", test.path))
   271  		if err != nil {
   272  			t.Errorf("failed to read %q: %v", test.path, err)
   273  			continue
   274  		}
   275  		var got Elements
   276  		err = json.Unmarshal(data, &got)
   277  		if err != nil {
   278  			t.Errorf("failed to unmarshal %q: %v", test.path, err)
   279  			continue
   280  		}
   281  
   282  		if len(got.Nodes) != test.wantNodes {
   283  			t.Errorf("unexpected result for order of %q: got:%d want:%d", test.path, len(got.Nodes), test.wantNodes)
   284  		}
   285  		if len(got.Edges) != test.wantEdges {
   286  			t.Errorf("unexpected result for size of %q: got:%d want:%d", test.path, len(got.Edges), test.wantEdges)
   287  		}
   288  		if test.wantGraph != nil {
   289  			if !reflect.DeepEqual(&got, test.wantGraph) {
   290  				t.Errorf("unexpected result for %q:\ngot:\n%#v\nwant:\n%#v", test.path, got.Nodes, test.wantGraph.Nodes)
   291  			}
   292  		} else {
   293  			if !reflect.DeepEqual(got.Nodes[0], test.firstNode) {
   294  				t.Errorf("unexpected result for %q:\ngot:\n%#v\nwant:\n%#v", test.path, got.Nodes[0], test.firstNode)
   295  			}
   296  			if !reflect.DeepEqual(got.Edges[0], test.firstEdge) {
   297  				t.Errorf("unexpected result for %q:\ngot:\n%v\nwant:\n%#v", test.path, got.Edges[0].Data.Source, test.firstEdge.Data.Source)
   298  			}
   299  		}
   300  		if test.wantNodeAttributes != nil {
   301  			var paths []string
   302  			for _, n := range got.Nodes {
   303  				paths = attrPaths(paths, "", n.Data.Attributes)
   304  			}
   305  			gotAttrs := make(map[string]bool)
   306  			for _, p := range paths {
   307  				gotAttrs[p] = true
   308  			}
   309  			if !reflect.DeepEqual(gotAttrs, test.wantNodeAttributes) {
   310  				t.Errorf("unexpected result for %q:\ngot:\n%#v\nwant:\n%#v", test.path, gotAttrs, test.wantNodeAttributes)
   311  			}
   312  		}
   313  		if test.wantEdgeAttributes != nil {
   314  			var paths []string
   315  			for _, e := range got.Edges {
   316  				paths = attrPaths(paths, "", e.Data.Attributes)
   317  			}
   318  			gotAttrs := make(map[string]bool)
   319  			for _, p := range paths {
   320  				gotAttrs[p] = true
   321  			}
   322  			if !reflect.DeepEqual(gotAttrs, test.wantEdgeAttributes) {
   323  				t.Errorf("unexpected result for %q:\ngot:\n%#v\nwant:\n%#v", test.path, gotAttrs, test.wantEdgeAttributes)
   324  			}
   325  		}
   326  	}
   327  }
   328  
   329  func TestMarshalNodeEdge(t *testing.T) {
   330  	for _, test := range cytoscapejsNodeEdgeTests {
   331  		data, err := os.ReadFile(filepath.Join("testdata", test.path))
   332  		if err != nil {
   333  			t.Errorf("failed to read %q: %v", test.path, err)
   334  			continue
   335  		}
   336  		var want Elements
   337  		err = json.Unmarshal(data, &want)
   338  		if err != nil {
   339  			t.Errorf("failed to unmarshal %q: %v", test.path, err)
   340  			continue
   341  		}
   342  		marshaled, err := json.Marshal(want)
   343  		if err != nil {
   344  			t.Errorf("failed to unmarshal %q: %v", test.path, err)
   345  			continue
   346  		}
   347  		var got Elements
   348  		err = json.Unmarshal(marshaled, &got)
   349  		if err != nil {
   350  			t.Errorf("failed to unmarshal %q: %v", test.path, err)
   351  			continue
   352  		}
   353  		if !reflect.DeepEqual(got, want) {
   354  			t.Errorf("unexpected result for %q:\ngot:\n%#v\nwant:\n%#v", test.path, got, want)
   355  		}
   356  	}
   357  }
   358  
   359  func attrPaths(dst []string, prefix string, m map[string]interface{}) []string {
   360  	for k, v := range m {
   361  		path := prefix
   362  		if path != "" {
   363  			path += "."
   364  		}
   365  		if v, ok := v.(map[string]interface{}); ok {
   366  			dst = attrPaths(dst, path+k, v)
   367  		}
   368  		dst = append(dst, path+k)
   369  	}
   370  	return dst
   371  }