graphs 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +1 -0
- data/lib/graph.rb +571 -0
- data/lib/graphs/gdf.rb +225 -0
- data/lib/graphs/json.rb +71 -0
- data/tests/edge_tests.rb +92 -0
- data/tests/gdf_tests.rb +377 -0
- data/tests/graph_tests.rb +703 -0
- data/tests/json_tests.rb +149 -0
- data/tests/node_tests.rb +141 -0
- data/tests/tests.rb +24 -0
- metadata +137 -0
- metadata.gz.sig +0 -0
data/lib/graphs/gdf.rb
ADDED
@@ -0,0 +1,225 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
# -*- coding: UTF-8 -*-
|
3
|
+
|
4
|
+
require 'csv'
|
5
|
+
require_relative '../graph'
|
6
|
+
|
7
|
+
class Graph
|
8
|
+
# Returns a GDF version of the current graph
|
9
|
+
# @param opts [Hash] A customizable set of options
|
10
|
+
# @return [String]
|
11
|
+
# @see GDF.unparse
|
12
|
+
def to_gdf(opts=nil)
|
13
|
+
GDF::unparse(self, opts)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Write the current graph into a GDF file. This method is used internally,
|
17
|
+
# use Graph#write instead.
|
18
|
+
# @param filename [String] a valid filename
|
19
|
+
# @return []
|
20
|
+
# @see GDF.unparse
|
21
|
+
def write_gdf(filename, opts=nil)
|
22
|
+
gdf = GDF::unparse(self, opts)
|
23
|
+
f = File.open(filename, 'w')
|
24
|
+
f.write(gdf)
|
25
|
+
f.close
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# GDF-related functions
|
30
|
+
# see http://guess.wikispot.org/The_GUESS_.gdf_format
|
31
|
+
module GDF
|
32
|
+
|
33
|
+
# Node fields definition
|
34
|
+
NODEDEF = 'nodedef>'
|
35
|
+
|
36
|
+
# Edge fields definition
|
37
|
+
EDGEDEF = 'edgedef>'
|
38
|
+
|
39
|
+
# Non-string predefined node properties
|
40
|
+
PREDEFINED_NODE_PROPS = {
|
41
|
+
'x' => 'float',
|
42
|
+
'y' => 'float',
|
43
|
+
'visible' => 'boolean',
|
44
|
+
'fixed' => 'boolean',
|
45
|
+
'style' => 'int',
|
46
|
+
'width' => 'float',
|
47
|
+
'height' => 'float'
|
48
|
+
}
|
49
|
+
|
50
|
+
# Non-string predefined edge properties
|
51
|
+
PREDEFINED_EDGE_PROPS = {
|
52
|
+
'visible' => 'boolean',
|
53
|
+
'weight' => 'float',
|
54
|
+
'width' => 'float',
|
55
|
+
'directed' => 'boolean',
|
56
|
+
'labelvisible' => 'boolean'
|
57
|
+
}
|
58
|
+
|
59
|
+
# Loads a GDF file and return a new Graph object
|
60
|
+
# @param filename [String] a valid filename
|
61
|
+
# @see GDF.parse
|
62
|
+
def self.load(filename)
|
63
|
+
self.parse(File.read(filename))
|
64
|
+
end
|
65
|
+
|
66
|
+
# Parse some GDF text and return a new Graph object
|
67
|
+
# @param content [String] a valid GDF String
|
68
|
+
# @see GDF.load
|
69
|
+
# @see GDF.unparse
|
70
|
+
def self.parse(content)
|
71
|
+
|
72
|
+
if (content.nil? || content.length == 0)
|
73
|
+
return Graph.new([],[])
|
74
|
+
end
|
75
|
+
|
76
|
+
fields_split = /[\t ]*,[\t ]*/
|
77
|
+
|
78
|
+
nodedef_len, edgedef_len = NODEDEF.length, EDGEDEF.length
|
79
|
+
|
80
|
+
current_def = nil
|
81
|
+
|
82
|
+
nodes, edges = [], []
|
83
|
+
current_set = nil
|
84
|
+
|
85
|
+
content.each_line do |line|
|
86
|
+
line.strip!
|
87
|
+
is_nodedef = line.start_with? NODEDEF
|
88
|
+
is_edgedef = !is_nodedef && line.start_with?(EDGEDEF)
|
89
|
+
|
90
|
+
if is_nodedef || is_edgedef
|
91
|
+
line.slice!(0, is_nodedef ? nodedef_len : edgedef_len)
|
92
|
+
line.strip!
|
93
|
+
defaults = is_nodedef ? PREDEFINED_NODE_PROPS : PREDEFINED_EDGE_PROPS
|
94
|
+
current_def = line.split(fields_split).map do |l|
|
95
|
+
read_def(l, defaults)
|
96
|
+
end
|
97
|
+
|
98
|
+
current_set = is_nodedef ? nodes : edges
|
99
|
+
else
|
100
|
+
el = {}
|
101
|
+
fields = line.parse_csv || [nil]
|
102
|
+
fields.zip(current_def).each do |val,label_type|
|
103
|
+
label, type, default = label_type
|
104
|
+
el[label] = parse_field(val, type, default)
|
105
|
+
end
|
106
|
+
current_set << el
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
Graph.new(nodes, edges)
|
111
|
+
end
|
112
|
+
|
113
|
+
# Return a GDF String which describe the given Graph
|
114
|
+
# @param graph [Graph]
|
115
|
+
# @param opts [Hash] A customizable set of options
|
116
|
+
# @return [String]
|
117
|
+
# @see Graph#write
|
118
|
+
def self.unparse(graph, opts=nil)
|
119
|
+
# nodes
|
120
|
+
gdf_s = NODEDEF
|
121
|
+
|
122
|
+
if (graph.nodes.length == 0)
|
123
|
+
return gdf_s
|
124
|
+
end
|
125
|
+
|
126
|
+
keys = graph.nodes[0].keys
|
127
|
+
nodedef = keys.map { |k| [k, self.get_type(graph.nodes[0][k], opts)] }
|
128
|
+
|
129
|
+
gdf_s += (nodedef.map {|nd| nd.join(' ')}).join(',') + "\n"
|
130
|
+
|
131
|
+
graph.nodes.each do |n|
|
132
|
+
gdf_s += n.values.to_csv
|
133
|
+
end
|
134
|
+
|
135
|
+
# edges
|
136
|
+
gdf_s += EDGEDEF
|
137
|
+
|
138
|
+
return gdf_s if graph.edges.empty?
|
139
|
+
|
140
|
+
keys = graph.edges[0].keys
|
141
|
+
edgedef = keys.map { |k| [k, self.get_type(graph.edges[0][k], opts)] }
|
142
|
+
|
143
|
+
gdf_s += (edgedef.map {|ed| ed.join(' ')}).join(',') + "\n"
|
144
|
+
|
145
|
+
graph.edges.each do |e|
|
146
|
+
gdf_s += e.values.to_csv
|
147
|
+
end
|
148
|
+
|
149
|
+
gdf_s
|
150
|
+
end
|
151
|
+
|
152
|
+
private
|
153
|
+
|
154
|
+
# Read the value of a node/edge field, and return the value's
|
155
|
+
# type (String)
|
156
|
+
# @param v
|
157
|
+
# @param opts [Hash]
|
158
|
+
# @return [String]
|
159
|
+
def self.get_type(v, opts=nil)
|
160
|
+
opts = opts || {}
|
161
|
+
|
162
|
+
if v.is_a?(Fixnum) || v.is_a?(Bignum)
|
163
|
+
if opts[:gephi] || v <= 2147483647
|
164
|
+
return 'INT'
|
165
|
+
else
|
166
|
+
return 'BIGINT'
|
167
|
+
end
|
168
|
+
elsif v.is_a?(TrueClass) || v.is_a?(FalseClass)
|
169
|
+
return 'BOOLEAN'
|
170
|
+
elsif v.is_a?(Float)
|
171
|
+
return 'FLOAT'
|
172
|
+
else
|
173
|
+
return 'VARCHAR'
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# read a node/edge def, and return a list where the first element is the
|
178
|
+
# label of the field, the second its type, and the third and last one its
|
179
|
+
# default value
|
180
|
+
# @param s
|
181
|
+
# @param defaults
|
182
|
+
def self.read_def(s, defaults={})
|
183
|
+
label, *params = s.split(/\s+/)
|
184
|
+
default = nil
|
185
|
+
|
186
|
+
if params.empty?
|
187
|
+
value_type = defaults[label.downcase] || 'VARCHAR'
|
188
|
+
else
|
189
|
+
value_type = params.shift
|
190
|
+
|
191
|
+
if params.shift == 'default'
|
192
|
+
default = parse_field(params.shift, value_type.downcase)
|
193
|
+
end
|
194
|
+
|
195
|
+
if /((tiny|small|medium|big)?int|integer)/i.match(value_type)
|
196
|
+
value_type = 'int'
|
197
|
+
elsif /(float|real|double)/i.match(value_type)
|
198
|
+
value_type = 'float'
|
199
|
+
elsif (value_type.downcase === 'boolean')
|
200
|
+
value_type = 'boolean'
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
[label, value_type, default]
|
205
|
+
end
|
206
|
+
|
207
|
+
# read a field and return its value
|
208
|
+
# @param f
|
209
|
+
# @param value_type [String]
|
210
|
+
# @param default
|
211
|
+
def self.parse_field(f, value_type, default=nil)
|
212
|
+
case value_type
|
213
|
+
when 'int' then (f || default).to_i
|
214
|
+
when 'float' then (f || default).to_f
|
215
|
+
when 'boolean' then
|
216
|
+
if f.nil?
|
217
|
+
default.nil? ? false : default
|
218
|
+
else
|
219
|
+
/^(?:null|false|)$/i !~ f
|
220
|
+
end
|
221
|
+
else f || default
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
end
|
data/lib/graphs/json.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
# -*- coding: UTF-8 -*-
|
3
|
+
|
4
|
+
require 'json'
|
5
|
+
require_relative '../graph'
|
6
|
+
|
7
|
+
class Graph
|
8
|
+
# Returns a JSON version of the current graph
|
9
|
+
# @param opts [Hash] A customizable set of options
|
10
|
+
# @return [String]
|
11
|
+
# @see JSONGraph.unparse
|
12
|
+
def to_json(opts=nil)
|
13
|
+
JSONGraph::unparse(self, opts)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Write the current graph into a JSON file. This method is used internally,
|
17
|
+
# use Graph#write instead.
|
18
|
+
# @param filename [String] a valid filename
|
19
|
+
# @return []
|
20
|
+
# @see JSON.unparse
|
21
|
+
def write_json(filename, opts=nil)
|
22
|
+
json = JSONGraph::unparse(self, opts)
|
23
|
+
f = File.open(filename, 'w')
|
24
|
+
f.write(json)
|
25
|
+
f.close
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# JSON-related functions
|
30
|
+
module JSONGraph
|
31
|
+
|
32
|
+
# Loads a JSON file and return a new Graph object
|
33
|
+
# @param filename [String] a valid filename
|
34
|
+
# @return [Graph]
|
35
|
+
# @see JSONGraph.parse
|
36
|
+
def self.load(filename)
|
37
|
+
self.parse(File.read(filename))
|
38
|
+
end
|
39
|
+
|
40
|
+
# Parse some JSON text and return a new Graph object
|
41
|
+
# @param content [String] a valid GDF String
|
42
|
+
# @return [Graph]
|
43
|
+
# @see JSONGraph.load
|
44
|
+
# @see JSONGraph.unparse
|
45
|
+
def self.parse(content)
|
46
|
+
|
47
|
+
if (content.nil? || content.length == 0)
|
48
|
+
return Graph.new([],[])
|
49
|
+
end
|
50
|
+
|
51
|
+
content = JSON.parse content
|
52
|
+
|
53
|
+
nodes = content['nodes']
|
54
|
+
edges = content['edges']
|
55
|
+
|
56
|
+
Graph.new(nodes, edges)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Return a JSON String which describe the given Graph
|
60
|
+
# @param graph [Graph]
|
61
|
+
# @param opts [Hash] A customizable set of options
|
62
|
+
# @return [String]
|
63
|
+
# @see Graph#write
|
64
|
+
def self.unparse(graph, opts=nil)
|
65
|
+
|
66
|
+
nodes = graph.nodes.map { |n| n.to_hash }
|
67
|
+
edges = graph.edges.map { |e| e.to_hash }
|
68
|
+
|
69
|
+
JSON.dump({ 'nodes' => nodes, 'edges' => edges })
|
70
|
+
end
|
71
|
+
end
|
data/tests/edge_tests.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
# -*- coding: UTF-8 -*-
|
3
|
+
|
4
|
+
class Edge_test < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@@empty = Graph::Node.new
|
8
|
+
@@alice = Graph::Node.new('label' => 'Alice')
|
9
|
+
|
10
|
+
# Alice ----> Bob
|
11
|
+
# ↑ ↑
|
12
|
+
# | |
|
13
|
+
# Oscar -------'
|
14
|
+
@@sample_graph = Graph.new(
|
15
|
+
[
|
16
|
+
{ 'label' => 'Alice' },
|
17
|
+
{ 'label' => 'Bob' },
|
18
|
+
{ 'label' => 'Oscar' }
|
19
|
+
],
|
20
|
+
[
|
21
|
+
{ 'node1' => 'Alice', 'node2' => 'Bob' },
|
22
|
+
{ :node1 => 'Oscar', 'node2' => 'Alice'},
|
23
|
+
{ 'node1' => 'Oscar', :node2 => 'Bob'}
|
24
|
+
]
|
25
|
+
)
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_edge_node1_attr
|
30
|
+
assert_equal('Alice', @@sample_graph.edges[0].node1)
|
31
|
+
assert_equal('Oscar', @@sample_graph.edges[1].node1)
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_edge_node2_attr
|
35
|
+
assert_equal('Alice', @@sample_graph.edges[1].node2)
|
36
|
+
assert_equal('Bob', @@sample_graph.edges[2].node2)
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_edge_update
|
40
|
+
|
41
|
+
e = Graph::Edge.new
|
42
|
+
|
43
|
+
assert_equal(true, e.update({}).is_a?(Graph::Edge))
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_edge_init_with_another_edge
|
47
|
+
|
48
|
+
e = Graph::Edge.new({ :foo => 'bar' })
|
49
|
+
|
50
|
+
assert_equal( e, Graph::Edge.new(e) )
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
class EdgeArray_test < Test::Unit::TestCase
|
57
|
+
|
58
|
+
def test_edgearray_push_edge
|
59
|
+
|
60
|
+
e = Graph::Edge.new({ :foo => 42 })
|
61
|
+
ea = Graph::EdgeArray.new([])
|
62
|
+
|
63
|
+
ea.push(e)
|
64
|
+
|
65
|
+
assert_equal(e, ea[0])
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_edgearray_push_hash
|
70
|
+
|
71
|
+
e = { :foo => 42 }
|
72
|
+
ea = Graph::EdgeArray.new([])
|
73
|
+
|
74
|
+
ea.push(e)
|
75
|
+
|
76
|
+
assert_equal(Graph::Edge.new(e), ea[0])
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_edgearray_push_no_edge_nor_hash
|
81
|
+
|
82
|
+
ea = Graph::EdgeArray.new([])
|
83
|
+
|
84
|
+
assert_raise(TypeError) do
|
85
|
+
|
86
|
+
ea.push(42)
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
data/tests/gdf_tests.rb
ADDED
@@ -0,0 +1,377 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
# -*- coding: UTF-8 -*-
|
3
|
+
|
4
|
+
module Utils
|
5
|
+
def self.get_sample_graph
|
6
|
+
@@sample_graph_1
|
7
|
+
end
|
8
|
+
|
9
|
+
@@sample_graph_1 = "nodedef>label VARCHAR,num INT,biglabel VARCHAR\n"
|
10
|
+
@@sample_graph_1 += "toto,14,TOTO\nlala,5,LALA\ntiti,988,TITI\n"
|
11
|
+
@@sample_graph_1 += "edgedef>node1 VARCHAR,node2 VARCHAR,directed BOOLEAN\n"
|
12
|
+
@@sample_graph_1 += "toto,lala,true\nlala,titi,true\n"
|
13
|
+
@@sample_graph_1 += "titi,lala,false\ntiti,toto,true\n"
|
14
|
+
end
|
15
|
+
|
16
|
+
class GDF_Graph_test < Test::Unit::TestCase
|
17
|
+
|
18
|
+
# == Graph#to_gdf == #
|
19
|
+
|
20
|
+
def test_empty_graph_to_gdf
|
21
|
+
g = Graph.new
|
22
|
+
empty_gdf = "nodedef>"
|
23
|
+
|
24
|
+
assert_equal(empty_gdf, g.to_gdf)
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_sample_graph_to_gdf
|
28
|
+
gdf = Utils::get_sample_graph
|
29
|
+
g = GDF::parse(gdf)
|
30
|
+
assert_equal(gdf, g.to_gdf)
|
31
|
+
end
|
32
|
+
|
33
|
+
# == Graph#write('….gdf') == #
|
34
|
+
|
35
|
+
def test_empty_graph_write_gdf
|
36
|
+
g = Graph.new
|
37
|
+
|
38
|
+
f = Tempfile.new([ 'foo', '.gdf' ])
|
39
|
+
f.close
|
40
|
+
|
41
|
+
g.write(f.path)
|
42
|
+
g2 = GDF.load(f.path)
|
43
|
+
|
44
|
+
assert_equal(g, g2)
|
45
|
+
f.unlink
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class GDF_test < Test::Unit::TestCase
|
50
|
+
|
51
|
+
# == GDF::parse == #
|
52
|
+
|
53
|
+
def test_parse_empty_graph
|
54
|
+
g = GDF::parse('')
|
55
|
+
|
56
|
+
assert_equal([], g.nodes)
|
57
|
+
assert_equal([], g.edges)
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_parse_space_after_nodedef
|
61
|
+
g = GDF::parse("nodedef> foo VARCHAR\nbar")
|
62
|
+
|
63
|
+
assert_equal(1, g.nodes.length)
|
64
|
+
assert_equal('bar', g.nodes[0]['foo'])
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_parse_quoted_value
|
68
|
+
code = <<EOC
|
69
|
+
nodedef>label VARCHAR
|
70
|
+
"foo"
|
71
|
+
EOC
|
72
|
+
|
73
|
+
g = GDF::parse(code)
|
74
|
+
assert_equal(1, g.nodes.length)
|
75
|
+
assert_equal('foo', g.nodes[0].attrs['label'])
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_parse_quoted_value_with_quote
|
79
|
+
code = <<EOC
|
80
|
+
nodedef>label VARCHAR
|
81
|
+
"fo""o"
|
82
|
+
EOC
|
83
|
+
|
84
|
+
g = GDF::parse(code)
|
85
|
+
assert_equal(1, g.nodes.length)
|
86
|
+
assert_equal('fo"o', g.nodes[0].attrs['label'])
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_parse_value_with_comma
|
90
|
+
code = <<EOC
|
91
|
+
nodedef>label VARCHAR,num INT
|
92
|
+
"foo,bar",42
|
93
|
+
EOC
|
94
|
+
|
95
|
+
g = GDF::parse(code)
|
96
|
+
assert_equal(1, g.nodes.length)
|
97
|
+
assert_equal('foo,bar', g.nodes[0].attrs['label'])
|
98
|
+
end
|
99
|
+
|
100
|
+
def test_parse_empty_graph_with_nodedef
|
101
|
+
|
102
|
+
s = "nodedef>label VARCHAR\n"
|
103
|
+
|
104
|
+
g = GDF::parse(s)
|
105
|
+
|
106
|
+
assert_equal([], g.nodes)
|
107
|
+
assert_equal([], g.edges)
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_parse_empty_graph_with_nodedef_and_edgedef
|
111
|
+
s = "nodedef>label VARCHAR\nedgedef>node1 VARCHAR,node2 VARCHAR\n"
|
112
|
+
g = GDF::parse(s)
|
113
|
+
|
114
|
+
assert_equal([], g.nodes)
|
115
|
+
assert_equal([], g.edges)
|
116
|
+
end
|
117
|
+
|
118
|
+
def test_parse_one_node_no_edge_string_field
|
119
|
+
s = "nodedef>label VARCHAR\nfoo\n"
|
120
|
+
g = GDF::parse(s)
|
121
|
+
|
122
|
+
assert_equal(1, g.nodes.length)
|
123
|
+
assert_equal('foo', g.nodes[0]['label'])
|
124
|
+
assert_equal([], g.edges)
|
125
|
+
end
|
126
|
+
|
127
|
+
def test_parse_one_node_no_edge_tinyint_field
|
128
|
+
s = "nodedef>num TINYINT\n3\n"
|
129
|
+
g = GDF::parse(s)
|
130
|
+
|
131
|
+
assert_equal(1, g.nodes.length)
|
132
|
+
assert_equal(3, g.nodes[0]['num'])
|
133
|
+
assert_equal([], g.edges)
|
134
|
+
end
|
135
|
+
|
136
|
+
def test_parse_one_node_no_edge_smallint_field
|
137
|
+
s = "nodedef>num SMALLINT\n3\n"
|
138
|
+
g = GDF::parse(s)
|
139
|
+
|
140
|
+
assert_equal(1, g.nodes.length)
|
141
|
+
assert_equal(3, g.nodes[0]['num'])
|
142
|
+
assert_equal([], g.edges)
|
143
|
+
end
|
144
|
+
|
145
|
+
def test_parse_one_node_no_edge_int_field
|
146
|
+
s = "nodedef>num INT\n3\n"
|
147
|
+
g = GDF::parse(s)
|
148
|
+
|
149
|
+
assert_equal(1, g.nodes.length)
|
150
|
+
assert_equal(3, g.nodes[0]['num'])
|
151
|
+
assert_equal([], g.edges)
|
152
|
+
end
|
153
|
+
|
154
|
+
def test_parse_one_node_no_edge_negative_int_field
|
155
|
+
s = "nodedef>num INT\n-1337\n"
|
156
|
+
g = GDF::parse(s)
|
157
|
+
|
158
|
+
assert_equal(1, g.nodes.length)
|
159
|
+
assert_equal(-1337, g.nodes[0]['num'])
|
160
|
+
assert_equal([], g.edges)
|
161
|
+
end
|
162
|
+
|
163
|
+
def test_parse_one_node_no_edge_bigint_field
|
164
|
+
s = "nodedef>num BIGINT\n3\n"
|
165
|
+
g = GDF::parse(s)
|
166
|
+
|
167
|
+
assert_equal(1, g.nodes.length)
|
168
|
+
assert_equal(3, g.nodes[0]['num'])
|
169
|
+
assert_equal([], g.edges)
|
170
|
+
end
|
171
|
+
|
172
|
+
def test_parse_one_node_no_edge_float_field
|
173
|
+
s = "nodedef>num FLOAT\n3\n"
|
174
|
+
g = GDF::parse(s)
|
175
|
+
|
176
|
+
assert_equal(1, g.nodes.length)
|
177
|
+
assert_equal(3.0, g.nodes[0]['num'])
|
178
|
+
assert_equal([], g.edges)
|
179
|
+
end
|
180
|
+
|
181
|
+
def test_parse_one_node_no_edge_double_field
|
182
|
+
s = "nodedef>num FLOAT\n3\n"
|
183
|
+
g = GDF::parse(s)
|
184
|
+
|
185
|
+
assert_equal(1, g.nodes.length)
|
186
|
+
assert_equal(3.0, g.nodes[0]['num'])
|
187
|
+
assert_equal([], g.edges)
|
188
|
+
end
|
189
|
+
|
190
|
+
def test_parse_one_node_no_edge_real_field
|
191
|
+
s = "nodedef>num FLOAT\n3\n"
|
192
|
+
g = GDF::parse(s)
|
193
|
+
|
194
|
+
assert_equal(1, g.nodes.length)
|
195
|
+
assert_equal(3.0, g.nodes[0]['num'])
|
196
|
+
assert_equal([], g.edges)
|
197
|
+
end
|
198
|
+
|
199
|
+
def test_parse_one_node_no_edge_negative_real_field
|
200
|
+
s = "nodedef>num FLOAT\n-42.14\n"
|
201
|
+
g = GDF::parse(s)
|
202
|
+
|
203
|
+
assert_equal(1, g.nodes.length)
|
204
|
+
assert_equal(-42.14, g.nodes[0]['num'])
|
205
|
+
assert_equal([], g.edges)
|
206
|
+
end
|
207
|
+
|
208
|
+
def test_parse_one_node_no_edge_unknow_field_type
|
209
|
+
s = "nodedef>foo BAR\nfoobar"
|
210
|
+
g = GDF::parse(s)
|
211
|
+
|
212
|
+
assert_equal(1, g.nodes.length)
|
213
|
+
assert_equal('foobar', g.nodes[0]['foo'])
|
214
|
+
assert_equal([], g.edges)
|
215
|
+
end
|
216
|
+
|
217
|
+
def test_parse_one_node_no_type
|
218
|
+
s = "nodedef>foo\nfoobar"
|
219
|
+
g = GDF::parse(s)
|
220
|
+
|
221
|
+
assert_equal(1, g.nodes.length)
|
222
|
+
assert_equal('foobar', g.nodes[0]['foo'])
|
223
|
+
assert_equal([], g.edges)
|
224
|
+
end
|
225
|
+
|
226
|
+
def test_parse_one_node_builtin_visible_prop_no_type
|
227
|
+
s = "nodedef>visible,foo VARCHAR\n,bar\n"
|
228
|
+
g = GDF::parse(s)
|
229
|
+
|
230
|
+
assert_equal(1, g.nodes.length)
|
231
|
+
assert_equal('bar', g.nodes[0]['foo'])
|
232
|
+
assert_equal(false, g.nodes[0]['visible'])
|
233
|
+
end
|
234
|
+
|
235
|
+
def test_parse_one_node_builtin_visible_prop_with_type
|
236
|
+
s = "nodedef>visible BOOLEAN,foo VARCHAR\n,bar\n"
|
237
|
+
g = GDF::parse(s)
|
238
|
+
|
239
|
+
assert_equal(1, g.nodes.length)
|
240
|
+
assert_equal('bar', g.nodes[0]['foo'])
|
241
|
+
assert_equal(false, g.nodes[0]['visible'])
|
242
|
+
end
|
243
|
+
|
244
|
+
def test_parse_one_node_truthy_boolean_value
|
245
|
+
s = "nodedef>xyz BOOLEAN,foo VARCHAR\nxfalse,bar\n"
|
246
|
+
g = GDF::parse(s)
|
247
|
+
|
248
|
+
assert_equal(1, g.nodes.length)
|
249
|
+
assert_equal('bar', g.nodes[0]['foo'])
|
250
|
+
assert_equal(true, g.nodes[0]['xyz'])
|
251
|
+
end
|
252
|
+
|
253
|
+
def test_parse_one_node_null_boolean_value
|
254
|
+
s = "nodedef>xyz BOOLEAN,foo VARCHAR\nnull,bar\n"
|
255
|
+
g = GDF::parse(s)
|
256
|
+
|
257
|
+
assert_equal(1, g.nodes.length)
|
258
|
+
assert_equal('bar', g.nodes[0]['foo'])
|
259
|
+
assert_equal(false, g.nodes[0]['xyz'])
|
260
|
+
end
|
261
|
+
|
262
|
+
def test_parse_nodes_only_one_empty_boolean_value
|
263
|
+
s = "nodedef>xyz BOOLEAN\n\n\n"
|
264
|
+
g = GDF::parse(s)
|
265
|
+
|
266
|
+
assert_equal(2, g.nodes.length)
|
267
|
+
assert_equal(false, g.nodes[0]['xyz'])
|
268
|
+
assert_equal(false, g.nodes[1]['xyz'])
|
269
|
+
end
|
270
|
+
|
271
|
+
def test_parse_nodes_default_int_value
|
272
|
+
s = "nodedef>xyz INT default 42,foo VARCHAR\n,bar\n12,foo"
|
273
|
+
g = GDF::parse(s)
|
274
|
+
|
275
|
+
assert_equal(2, g.nodes.length)
|
276
|
+
assert_equal(42, g.nodes[0]['xyz'])
|
277
|
+
assert_equal(12, g.nodes[1]['xyz'])
|
278
|
+
end
|
279
|
+
|
280
|
+
def test_parse_nodes_default_boolean_value_true
|
281
|
+
s = "nodedef>xyz BOOLEAN default true,foo VARCHAR\n,bar\nfalse,foo"
|
282
|
+
g = GDF::parse(s)
|
283
|
+
|
284
|
+
assert_equal(2, g.nodes.length)
|
285
|
+
assert_equal(true, g.nodes[0]['xyz'])
|
286
|
+
assert_equal(false, g.nodes[1]['xyz'])
|
287
|
+
end
|
288
|
+
|
289
|
+
def test_parse_nodes_default_boolean_value_false
|
290
|
+
s = "nodedef>xyz BOOLEAN default false,foo VARCHAR\n,bar\ntrue,foo"
|
291
|
+
g = GDF::parse(s)
|
292
|
+
|
293
|
+
assert_equal(2, g.nodes.length)
|
294
|
+
assert_equal(false, g.nodes[0]['xyz'])
|
295
|
+
assert_equal(true, g.nodes[1]['xyz'])
|
296
|
+
end
|
297
|
+
|
298
|
+
def test_parse_one_edge_no_type
|
299
|
+
s = "nodedef>label VARCHAR\nfoobar\nedgedef>node1,node2\nfoobar,foobar"
|
300
|
+
g = GDF::parse(s)
|
301
|
+
|
302
|
+
assert_equal(1, g.nodes.length)
|
303
|
+
assert_equal('foobar', g.nodes[0]['label'])
|
304
|
+
assert_equal('foobar', g.edges[0]['node1'])
|
305
|
+
assert_equal('foobar', g.edges[0]['node2'])
|
306
|
+
end
|
307
|
+
|
308
|
+
def test_parse_sample_graph
|
309
|
+
g = GDF::parse(Utils::get_sample_graph)
|
310
|
+
|
311
|
+
assert_equal(3, g.nodes.length)
|
312
|
+
assert_equal(4, g.edges.length)
|
313
|
+
|
314
|
+
assert_equal('toto', g.nodes[0]['label'])
|
315
|
+
assert_equal('TOTO', g.nodes[0]['biglabel'])
|
316
|
+
assert_equal(988, g.nodes[2]['num'])
|
317
|
+
|
318
|
+
assert_equal('toto', g.edges[0]['node1'])
|
319
|
+
assert_equal('lala', g.edges[0]['node2'])
|
320
|
+
assert_equal(false, g.edges[2]['directed'])
|
321
|
+
|
322
|
+
end
|
323
|
+
|
324
|
+
# == GDF::unparse == #
|
325
|
+
|
326
|
+
def test_unparse_empty_graph
|
327
|
+
g = Graph.new
|
328
|
+
|
329
|
+
s = GDF::unparse(g)
|
330
|
+
|
331
|
+
assert_equal("nodedef>", s)
|
332
|
+
end
|
333
|
+
|
334
|
+
def test_unparse_sample_graph
|
335
|
+
g1 = GDF::parse(Utils::get_sample_graph)
|
336
|
+
g2 = GDF::parse(GDF::unparse(g1))
|
337
|
+
|
338
|
+
assert_equal(g1, g2)
|
339
|
+
end
|
340
|
+
|
341
|
+
def test_unparse_big_int
|
342
|
+
n = 2**40
|
343
|
+
g = Graph.new([{'n'=>n}])
|
344
|
+
gdf = GDF::unparse(g)
|
345
|
+
|
346
|
+
assert_equal("nodedef>n BIGINT\n#{n}\nedgedef>", gdf)
|
347
|
+
end
|
348
|
+
|
349
|
+
def test_unparse_big_int_gephi
|
350
|
+
n = 2**40
|
351
|
+
g = Graph.new([{'n'=>n}])
|
352
|
+
gdf = GDF::unparse(g, {:gephi=>true})
|
353
|
+
|
354
|
+
assert_equal("nodedef>n INT\n#{n}\nedgedef>", gdf)
|
355
|
+
end
|
356
|
+
|
357
|
+
def test_unparse_float_field
|
358
|
+
g = Graph.new([{ 'n' => 3.14 }])
|
359
|
+
gdf = GDF::unparse(g)
|
360
|
+
|
361
|
+
assert_equal("nodedef>n FLOAT\n3.14\nedgedef>", gdf)
|
362
|
+
end
|
363
|
+
|
364
|
+
def test_unparse_value_with_quote
|
365
|
+
g = Graph.new([{'n' => 'foo"bar'}])
|
366
|
+
gdf = GDF::unparse(g)
|
367
|
+
|
368
|
+
assert_equal("nodedef>n VARCHAR\n\"foo\"\"bar\"\nedgedef>", gdf)
|
369
|
+
end
|
370
|
+
|
371
|
+
def test_unparse_value_with_comma
|
372
|
+
g = Graph.new([{'n' => 'foo,bar'}])
|
373
|
+
gdf = GDF::unparse(g)
|
374
|
+
|
375
|
+
assert_equal("nodedef>n VARCHAR\n\"foo,bar\"\nedgedef>", gdf)
|
376
|
+
end
|
377
|
+
end
|