graphs 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
@@ -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
@@ -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
@@ -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