// Copyright ©2018 The Gonum Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package cytoscapejs import ( "encoding/json" "os" "path/filepath" "reflect" "testing" ) var cytoscapejsElementsTests = []struct { path string wantNodes int wantEdges int wantGraph []Element wantAttributes []string }{ { path: "edge-type.json", wantNodes: 10, wantEdges: 10, wantGraph: []Element{ {Data: ElemData{ID: "n01", Attributes: map[string]interface{}{"type": "bezier"}}}, {Data: ElemData{ID: "n02"}}, {Data: ElemData{ID: "e01", Source: "n01", Target: "n02"}, Classes: "bezier"}, {Data: ElemData{ID: "e02", Source: "n01", Target: "n02"}, Classes: "bezier"}, {Data: ElemData{ID: "e03", Source: "n02", Target: "n01"}, Classes: "bezier"}, {Data: ElemData{ID: "n03", Attributes: map[string]interface{}{"type": "unbundled-bezier"}}}, {Data: ElemData{ID: "n04"}}, {Data: ElemData{ID: "e04", Source: "n03", Target: "n04"}, Classes: "unbundled-bezier"}, {Data: ElemData{ID: "n05", Attributes: map[string]interface{}{"type": "unbundled-bezier(multiple)"}}}, {Data: ElemData{ID: "n06"}}, {Data: ElemData{ID: "e05", Source: "n05", Target: "n06", Parent: ""}, Classes: "multi-unbundled-bezier"}, {Data: ElemData{ID: "n07", Attributes: map[string]interface{}{"type": "haystack"}}}, {Data: ElemData{ID: "n08"}}, {Data: ElemData{ID: "e06", Source: "n08", Target: "n07"}, Classes: "haystack"}, {Data: ElemData{ID: "e07", Source: "n08", Target: "n07"}, Classes: "haystack"}, {Data: ElemData{ID: "e08", Source: "n08", Target: "n07"}, Classes: "haystack"}, {Data: ElemData{ID: "e09", Source: "n08", Target: "n07"}, Classes: "haystack"}, {Data: ElemData{ID: "n09", Attributes: map[string]interface{}{"type": "segments"}}}, {Data: ElemData{ID: "n10"}}, {Data: ElemData{ID: "e10", Source: "n09", Target: "n10"}, Classes: "segments"}, }, }, } func TestUnmarshalElements(t *testing.T) { for _, test := range cytoscapejsElementsTests { data, err := os.ReadFile(filepath.Join("testdata", test.path)) if err != nil { t.Errorf("failed to read %q: %v", test.path, err) continue } var got []Element err = json.Unmarshal(data, &got) if err != nil { t.Errorf("failed to unmarshal %q: %v", test.path, err) continue } var gotNodes, gotEdges int for _, e := range got { typ, err := e.Type() if err != nil { t.Errorf("unexpected error finding element type for %+v: %v", e, err) } switch typ { case NodeElement: gotNodes++ case EdgeElement: gotEdges++ } } if gotNodes != test.wantNodes { t.Errorf("unexpected result for order of %q: got:%d want:%d", test.path, gotNodes, test.wantNodes) } if gotEdges != test.wantEdges { t.Errorf("unexpected result for size of %q: got:%d want:%d", test.path, gotEdges, test.wantEdges) } if test.wantGraph != nil && !reflect.DeepEqual(got, test.wantGraph) { t.Errorf("unexpected result for %q:\ngot:\n%#v\nwant:\n%#v", test.path, got, test.wantGraph) } } } func TestMarshalElements(t *testing.T) { for _, test := range cytoscapejsElementsTests { data, err := os.ReadFile(filepath.Join("testdata", test.path)) if err != nil { t.Errorf("failed to read %q: %v", test.path, err) continue } var want []Element err = json.Unmarshal(data, &want) if err != nil { t.Errorf("failed to unmarshal %q: %v", test.path, err) continue } marshaled, err := json.Marshal(want) if err != nil { t.Errorf("failed to unmarshal %q: %v", test.path, err) continue } var got []Element err = json.Unmarshal(marshaled, &got) if err != nil { t.Errorf("failed to unmarshal %q: %v", test.path, err) continue } if !reflect.DeepEqual(got, want) { t.Errorf("unexpected result for %q:\ngot:\n%#v\nwant:\n%#v", test.path, got, want) } } } var cytoscapejsNodeEdgeTests = []struct { path string wantNodes int wantEdges int wantGraph *Elements firstNode Node firstEdge Edge wantNodeAttributes map[string]bool wantEdgeAttributes map[string]bool }{ { path: "cola-compound.json", wantNodes: 9, wantEdges: 7, wantGraph: &Elements{ Nodes: []Node{ {Data: NodeData{ID: "compound-1", Parent: ""}}, {Data: NodeData{ID: "compound-2", Parent: ""}}, {Data: NodeData{ID: "compound-3", Parent: ""}}, {Data: NodeData{ID: "b", Parent: "compound-1"}}, {Data: NodeData{ID: "c", Parent: "compound-1"}}, {Data: NodeData{ID: "a", Parent: "compound-2"}}, {Data: NodeData{ID: "d", Parent: "compound-3"}}, {Data: NodeData{ID: "e", Parent: "compound-3"}}, {Data: NodeData{ID: "f", Parent: ""}}, }, Edges: []Edge{ {Data: EdgeData{ID: "ab", Source: "a", Target: "b"}}, {Data: EdgeData{ID: "bc", Source: "b", Target: "c"}}, {Data: EdgeData{ID: "ac", Source: "a", Target: "c"}}, {Data: EdgeData{ID: "cd", Source: "c", Target: "d"}}, {Data: EdgeData{ID: "de", Source: "d", Target: "e"}}, {Data: EdgeData{ID: "df", Source: "d", Target: "f"}}, {Data: EdgeData{ID: "af", Source: "a", Target: "f"}}, }, }, }, { path: "tokyo-railways.json", wantNodes: 943, wantEdges: 860, firstNode: Node{ Data: NodeData{ ID: "8220", Attributes: map[string]interface{}{ "station_name": "京成高砂", "close_ymd": "", "lon": 139.866875, "post": "", "e_status": 0.0, "SUID": 8220.0, "station_g_cd": 2300110.0, "add": "東京都葛飾区高砂五丁目28-1", "line_cd": 99340.0, "selected": false, "open_ymd": "", "name": "9934001", "pref_name": "東京都", "shared_name": "9934001", "lat": 35.750932, "x": 1398668.75, "y": -357509.32, }, }, Position: &Position{ X: 1398668.75, Y: -357509.32, }, }, firstEdge: Edge{ Data: EdgeData{ ID: "18417", Source: "8220", Target: "8221", Attributes: map[string]interface{}{ "line_name_k": "ホクソウテツドウホクソウセン", "is_bullet": false, "lon": 140.03784499075186, "company_name_k": "ホクソウテツドウ", "zoom": 11.0, "SUID": 18417.0, "company_type": 0.0, "company_name_h": "北総鉄道株式会社", "interaction": "99340", "shared_interaction": "99340", "company_url": "http://www.hokuso-railway.co.jp/", "line_name": "北総鉄道北総線", "selected": false, "company_name": "北総鉄道", "company_cd": 152.0, "name": "9934001 (99340) 9934002", "rr_cd": 99.0, "company_name_r": "北総鉄道", "e_status_x": 0.0, "shared_name": "9934001 (99340) 9934002", "lat": 35.78346285846615, "e_status_y": 0.0, "line_name_h": "北総鉄道北総線", }, }, }, wantNodeAttributes: map[string]bool{ "station_name": true, "close_ymd": true, "lon": true, "post": true, "e_status": true, "SUID": true, "station_g_cd": true, "add": true, "line_cd": true, "selected": true, "open_ymd": true, "name": true, "pref_name": true, "shared_name": true, "lat": true, "x": true, "y": true, }, wantEdgeAttributes: map[string]bool{ "line_name_k": true, "is_bullet": true, "lon": true, "company_name_k": true, "zoom": true, "SUID": true, "company_type": true, "company_name_h": true, "interaction": true, "shared_interaction": true, "company_url": true, "line_name": true, "selected": true, "company_name": true, "company_cd": true, "name": true, "rr_cd": true, "company_name_r": true, "e_status_x": true, "shared_name": true, "lat": true, "e_status_y": true, "line_name_h": true, }, }, } func TestUnmarshalNodeEdge(t *testing.T) { for _, test := range cytoscapejsNodeEdgeTests { data, err := os.ReadFile(filepath.Join("testdata", test.path)) if err != nil { t.Errorf("failed to read %q: %v", test.path, err) continue } var got Elements err = json.Unmarshal(data, &got) if err != nil { t.Errorf("failed to unmarshal %q: %v", test.path, err) continue } if len(got.Nodes) != test.wantNodes { t.Errorf("unexpected result for order of %q: got:%d want:%d", test.path, len(got.Nodes), test.wantNodes) } if len(got.Edges) != test.wantEdges { t.Errorf("unexpected result for size of %q: got:%d want:%d", test.path, len(got.Edges), test.wantEdges) } if test.wantGraph != nil { if !reflect.DeepEqual(&got, test.wantGraph) { t.Errorf("unexpected result for %q:\ngot:\n%#v\nwant:\n%#v", test.path, got.Nodes, test.wantGraph.Nodes) } } else { if !reflect.DeepEqual(got.Nodes[0], test.firstNode) { t.Errorf("unexpected result for %q:\ngot:\n%#v\nwant:\n%#v", test.path, got.Nodes[0], test.firstNode) } if !reflect.DeepEqual(got.Edges[0], test.firstEdge) { t.Errorf("unexpected result for %q:\ngot:\n%v\nwant:\n%#v", test.path, got.Edges[0].Data.Source, test.firstEdge.Data.Source) } } if test.wantNodeAttributes != nil { var paths []string for _, n := range got.Nodes { paths = attrPaths(paths, "", n.Data.Attributes) } gotAttrs := make(map[string]bool) for _, p := range paths { gotAttrs[p] = true } if !reflect.DeepEqual(gotAttrs, test.wantNodeAttributes) { t.Errorf("unexpected result for %q:\ngot:\n%#v\nwant:\n%#v", test.path, gotAttrs, test.wantNodeAttributes) } } if test.wantEdgeAttributes != nil { var paths []string for _, e := range got.Edges { paths = attrPaths(paths, "", e.Data.Attributes) } gotAttrs := make(map[string]bool) for _, p := range paths { gotAttrs[p] = true } if !reflect.DeepEqual(gotAttrs, test.wantEdgeAttributes) { t.Errorf("unexpected result for %q:\ngot:\n%#v\nwant:\n%#v", test.path, gotAttrs, test.wantEdgeAttributes) } } } } func TestMarshalNodeEdge(t *testing.T) { for _, test := range cytoscapejsNodeEdgeTests { data, err := os.ReadFile(filepath.Join("testdata", test.path)) if err != nil { t.Errorf("failed to read %q: %v", test.path, err) continue } var want Elements err = json.Unmarshal(data, &want) if err != nil { t.Errorf("failed to unmarshal %q: %v", test.path, err) continue } marshaled, err := json.Marshal(want) if err != nil { t.Errorf("failed to unmarshal %q: %v", test.path, err) continue } var got Elements err = json.Unmarshal(marshaled, &got) if err != nil { t.Errorf("failed to unmarshal %q: %v", test.path, err) continue } if !reflect.DeepEqual(got, want) { t.Errorf("unexpected result for %q:\ngot:\n%#v\nwant:\n%#v", test.path, got, want) } } } func attrPaths(dst []string, prefix string, m map[string]interface{}) []string { for k, v := range m { path := prefix if path != "" { path += "." } if v, ok := v.(map[string]interface{}); ok { dst = attrPaths(dst, path+k, v) } dst = append(dst, path+k) } return dst }