pio 0.10.1 → 0.11.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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/README.md +33 -2
  4. data/examples/echo_read.rb +1 -1
  5. data/examples/features_read.rb +1 -1
  6. data/examples/flow_mod_new.rb +13 -0
  7. data/examples/flow_mod_read.rb +6 -0
  8. data/features/echo_read.feature +27 -3
  9. data/features/features_read.feature +46 -2
  10. data/features/flow_mod_read.feature +186 -0
  11. data/features/hello_read.feature +9 -0
  12. data/features/packet_data/flow_mod_add.raw +0 -0
  13. data/features/packet_data/flow_mod_delete.raw +0 -0
  14. data/features/packet_data/flow_mod_delete_strict.raw +0 -0
  15. data/features/packet_data/flow_mod_modify.raw +0 -0
  16. data/features/packet_data/flow_mod_modify_strict.raw +0 -0
  17. data/features/packet_in_read.feature +13 -0
  18. data/features/packet_out_read.feature +16 -0
  19. data/features/step_definitions/packet_data_steps.rb +10 -1
  20. data/lib/pio.rb +1 -0
  21. data/lib/pio/echo.rb +10 -8
  22. data/lib/pio/enqueue.rb +1 -1
  23. data/lib/pio/features.rb +64 -7
  24. data/lib/pio/flow_mod.rb +86 -0
  25. data/lib/pio/hello.rb +4 -74
  26. data/lib/pio/ipv4_address.rb +1 -1
  27. data/lib/pio/match.rb +167 -0
  28. data/lib/pio/open_flow.rb +1 -0
  29. data/lib/pio/open_flow/actions.rb +65 -0
  30. data/lib/pio/open_flow/flags.rb +12 -9
  31. data/lib/pio/open_flow/message.rb +105 -21
  32. data/lib/pio/open_flow/phy_port.rb +31 -27
  33. data/lib/pio/open_flow/type.rb +1 -0
  34. data/lib/pio/packet_in.rb +2 -12
  35. data/lib/pio/packet_out.rb +4 -74
  36. data/lib/pio/send_out_port.rb +1 -0
  37. data/lib/pio/type/ip_address.rb +2 -7
  38. data/lib/pio/version.rb +1 -1
  39. data/pio.gemspec +8 -8
  40. data/spec/pio/echo/reply_spec.rb +61 -6
  41. data/spec/pio/echo/request_spec.rb +61 -6
  42. data/spec/pio/features/reply_spec.rb +81 -4
  43. data/spec/pio/features/request_spec.rb +88 -41
  44. data/spec/pio/flow_mod_spec.rb +151 -0
  45. data/spec/pio/hello_spec.rb +73 -56
  46. data/spec/pio/match_spec.rb +194 -0
  47. data/spec/pio/packet_in_spec.rb +35 -38
  48. data/spec/pio/packet_out_spec.rb +243 -182
  49. data/spec/pio/wildcards_spec.rb +115 -0
  50. metadata +37 -30
  51. data/features/packet_data/echo.raw +0 -0
  52. data/lib/pio/echo/message.rb +0 -49
  53. data/lib/pio/echo/reply.rb +0 -41
  54. data/lib/pio/echo/request.rb +0 -43
  55. data/lib/pio/features/reply.rb +0 -88
  56. data/lib/pio/features/request.rb +0 -68
  57. data/lib/pio/open_flow/parser.rb +0 -22
  58. data/spec/pio/echo_spec.rb +0 -49
  59. data/spec/pio/features_spec.rb +0 -96
@@ -1,6 +1,7 @@
1
1
  require 'bindata'
2
2
  require 'forwardable'
3
3
  require 'pio/monkey_patch/integer'
4
+ require 'pio/open_flow/port_number'
4
5
 
5
6
  module Pio
6
7
  # An action to output a packet to a port.
@@ -8,16 +8,11 @@ module Pio
8
8
  array :octets, type: :uint8, initial_length: 4
9
9
 
10
10
  def set(value)
11
- case value
12
- when String
13
- self.octets = value.split('.').map(&:to_i)
14
- else
15
- self.octets = value
16
- end
11
+ self.octets = IPv4Address.new(value).to_a
17
12
  end
18
13
 
19
14
  def get
20
- IPv4Address.new octets.map { |each| format('%d', each) }.join('.')
15
+ IPv4Address.new(octets.map { |each| format('%d', each) }.join('.'))
21
16
  end
22
17
 
23
18
  def ==(other)
data/lib/pio/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # Base module.
2
2
  module Pio
3
3
  # gem version.
4
- VERSION = '0.10.1'.freeze
4
+ VERSION = '0.11.0'.freeze
5
5
  end
data/pio.gemspec CHANGED
@@ -37,11 +37,11 @@ Gem::Specification.new do |gem|
37
37
  gem.add_dependency 'bindata', '~> 2.1.0'
38
38
 
39
39
  gem.add_development_dependency 'rake'
40
- gem.add_development_dependency 'bundler', '~> 1.7.11'
40
+ gem.add_development_dependency 'bundler', '~> 1.7.12'
41
41
  gem.add_development_dependency 'pry', '~> 0.10.1'
42
42
 
43
43
  # Guard
44
- gem.add_development_dependency 'guard', '~> 2.10.5'
44
+ gem.add_development_dependency 'guard', '~> 2.11.1'
45
45
  gem.add_development_dependency 'guard-bundler', '~> 2.1.0'
46
46
  gem.add_development_dependency 'guard-cucumber', '~> 1.5.3'
47
47
  gem.add_development_dependency 'guard-rspec', '~> 4.5.0'
@@ -52,17 +52,17 @@ Gem::Specification.new do |gem|
52
52
  gem.add_development_dependency 'terminal-notifier-guard', '~> 1.6.4'
53
53
 
54
54
  # Docs
55
- gem.add_development_dependency 'inch', '~> 0.5.8'
55
+ gem.add_development_dependency 'inch', '~> 0.5.10'
56
56
  gem.add_development_dependency 'yard', '~> 0.8.7.6'
57
57
 
58
58
  # Test
59
- gem.add_development_dependency 'codeclimate-test-reporter', '~> 0.4.4'
59
+ gem.add_development_dependency 'codeclimate-test-reporter', '~> 0.4.5'
60
60
  gem.add_development_dependency 'coveralls', '~> 0.7.2'
61
61
  gem.add_development_dependency 'cucumber', '~> 1.3.18'
62
- gem.add_development_dependency 'flay', '~> 2.5.0'
63
- gem.add_development_dependency 'flog', '~> 4.3.0'
64
- gem.add_development_dependency 'reek', '~> 1.5.1'
62
+ gem.add_development_dependency 'flay', '~> 2.6.0'
63
+ gem.add_development_dependency 'flog', '~> 4.3.1'
64
+ gem.add_development_dependency 'reek', '~> 1.6.2'
65
65
  gem.add_development_dependency 'rspec', '~> 3.1.0'
66
- gem.add_development_dependency 'rspec-given', '~> 3.5.4'
66
+ gem.add_development_dependency 'rspec-given', '~> 3.6.0'
67
67
  gem.add_development_dependency 'rubocop', '~> 0.28.0'
68
68
  end
@@ -1,6 +1,34 @@
1
1
  require 'pio/echo'
2
2
 
3
3
  describe Pio::Echo::Reply do
4
+ describe '.read' do
5
+ When(:echo_reply) { Pio::Echo::Reply.read(binary) }
6
+
7
+ context 'with an echo reply message' do
8
+ Given(:binary) { [1, 3, 0, 8, 0, 0, 0, 0].pack('C*') }
9
+
10
+ Then { echo_reply.ofp_version == 1 }
11
+ Then { echo_reply.message_type == Pio::OpenFlow::Type::ECHO_REPLY }
12
+ Then { echo_reply.message_length == 8 }
13
+ Then { echo_reply.transaction_id == 0 }
14
+ Then { echo_reply.xid == 0 }
15
+ Then { echo_reply.user_data.empty? }
16
+ Then { echo_reply.user_data == '' }
17
+ Then { echo_reply.body.empty? }
18
+ Then { echo_reply.body == '' }
19
+ Then { echo_reply.to_binary == [1, 3, 0, 8, 0, 0, 0, 0].pack('C*') }
20
+ end
21
+
22
+ context 'with a hello message' do
23
+ Given(:binary) { [1, 0, 0, 8, 0, 0, 0, 0].pack('C*') }
24
+
25
+ Then do
26
+ echo_reply ==
27
+ Failure(Pio::ParseError, 'Invalid Echo Reply message.')
28
+ end
29
+ end
30
+ end
31
+
4
32
  describe '.new' do
5
33
  context 'with no arguments' do
6
34
  When(:echo_reply) { Pio::Echo::Reply.new }
@@ -10,7 +38,10 @@ describe Pio::Echo::Reply do
10
38
  Then { echo_reply.message_length == 8 }
11
39
  Then { echo_reply.transaction_id == 0 }
12
40
  Then { echo_reply.xid == 0 }
41
+ Then { echo_reply.user_data.empty? }
13
42
  Then { echo_reply.user_data == '' }
43
+ Then { echo_reply.body.empty? }
44
+ Then { echo_reply.body == '' }
14
45
  Then { echo_reply.to_binary == [1, 3, 0, 8, 0, 0, 0, 0].pack('C*') }
15
46
  end
16
47
 
@@ -22,16 +53,13 @@ describe Pio::Echo::Reply do
22
53
  Then { echo_reply.message_length == 8 }
23
54
  Then { echo_reply.transaction_id == 123 }
24
55
  Then { echo_reply.xid == 123 }
56
+ Then { echo_reply.user_data.empty? }
25
57
  Then { echo_reply.user_data == '' }
58
+ Then { echo_reply.body.empty? }
59
+ Then { echo_reply.body == '' }
26
60
  Then { echo_reply.to_binary == [1, 3, 0, 8, 0, 0, 0, 123].pack('C*') }
27
61
  end
28
62
 
29
- context 'with 2**32' do
30
- When(:result) { Pio::Echo::Reply.new(2**32) }
31
-
32
- Then { result == Failure(ArgumentError) }
33
- end
34
-
35
63
  context 'with transaction_id: 123' do
36
64
  When(:echo_reply) { Pio::Echo::Reply.new(transaction_id: 123) }
37
65
 
@@ -40,7 +68,10 @@ describe Pio::Echo::Reply do
40
68
  Then { echo_reply.message_length == 8 }
41
69
  Then { echo_reply.transaction_id == 123 }
42
70
  Then { echo_reply.xid == 123 }
71
+ Then { echo_reply.user_data.empty? }
43
72
  Then { echo_reply.user_data == '' }
73
+ Then { echo_reply.body.empty? }
74
+ Then { echo_reply.body == '' }
44
75
  Then { echo_reply.to_binary == [1, 3, 0, 8, 0, 0, 0, 123].pack('C*') }
45
76
  end
46
77
 
@@ -52,7 +83,10 @@ describe Pio::Echo::Reply do
52
83
  Then { echo_reply.message_length == 8 }
53
84
  Then { echo_reply.transaction_id == 123 }
54
85
  Then { echo_reply.xid == 123 }
86
+ Then { echo_reply.user_data.empty? }
55
87
  Then { echo_reply.user_data == '' }
88
+ Then { echo_reply.body.empty? }
89
+ Then { echo_reply.body == '' }
56
90
  Then { echo_reply.to_binary == [1, 3, 0, 8, 0, 0, 0, 123].pack('C*') }
57
91
  end
58
92
 
@@ -65,12 +99,33 @@ describe Pio::Echo::Reply do
65
99
  Then { echo_reply.transaction_id == 123 }
66
100
  Then { echo_reply.xid == 123 }
67
101
  Then { echo_reply.user_data == 'foobar' }
102
+ Then { echo_reply.body == 'foobar' }
68
103
  Then do
69
104
  echo_reply.to_binary ==
70
105
  [1, 3, 0, 14, 0, 0, 0, 123, 102, 111, 111, 98, 97, 114].pack('C*')
71
106
  end
72
107
  end
73
108
 
109
+ context 'with -1' do
110
+ When(:result) { Pio::Echo::Reply.new(-1) }
111
+
112
+ Then do
113
+ result ==
114
+ Failure(ArgumentError,
115
+ 'Transaction ID should be an unsigned 32-bit integer.')
116
+ end
117
+ end
118
+
119
+ context 'with 2**32' do
120
+ When(:result) { Pio::Echo::Reply.new(2**32) }
121
+
122
+ Then do
123
+ result ==
124
+ Failure(ArgumentError,
125
+ 'Transaction ID should be an unsigned 32-bit integer.')
126
+ end
127
+ end
128
+
74
129
  context 'with :INVALID_ARGUMENT' do
75
130
  When(:result) { Pio::Echo::Reply.new(:INVALID_ARGUMENT) }
76
131
 
@@ -1,6 +1,34 @@
1
1
  require 'pio/echo'
2
2
 
3
3
  describe Pio::Echo::Request do
4
+ describe '.read' do
5
+ When(:echo_request) { Pio::Echo::Request.read(binary) }
6
+
7
+ context 'with an echo request message' do
8
+ Given(:binary) { [1, 2, 0, 8, 0, 0, 0, 0].pack('C*') }
9
+
10
+ Then { echo_request.ofp_version == 1 }
11
+ Then { echo_request.message_type == Pio::OpenFlow::Type::ECHO_REQUEST }
12
+ Then { echo_request.message_length == 8 }
13
+ Then { echo_request.transaction_id == 0 }
14
+ Then { echo_request.xid == 0 }
15
+ Then { echo_request.user_data.empty? }
16
+ Then { echo_request.user_data == '' }
17
+ Then { echo_request.body.empty? }
18
+ Then { echo_request.body == '' }
19
+ Then { echo_request.to_binary == [1, 2, 0, 8, 0, 0, 0, 0].pack('C*') }
20
+ end
21
+
22
+ context 'with a hello message' do
23
+ Given(:binary) { [1, 0, 0, 8, 0, 0, 0, 0].pack('C*') }
24
+
25
+ Then do
26
+ echo_request ==
27
+ Failure(Pio::ParseError, 'Invalid Echo Request message.')
28
+ end
29
+ end
30
+ end
31
+
4
32
  describe '.new' do
5
33
  context 'with no arguments' do
6
34
  When(:echo_request) { Pio::Echo::Request.new }
@@ -11,6 +39,9 @@ describe Pio::Echo::Request do
11
39
  Then { echo_request.transaction_id == 0 }
12
40
  Then { echo_request.xid == 0 }
13
41
  Then { echo_request.user_data.empty? }
42
+ Then { echo_request.user_data == '' }
43
+ Then { echo_request.body.empty? }
44
+ Then { echo_request.body == '' }
14
45
  Then { echo_request.to_binary == [1, 2, 0, 8, 0, 0, 0, 0].pack('C*') }
15
46
  end
16
47
 
@@ -23,15 +54,12 @@ describe Pio::Echo::Request do
23
54
  Then { echo_request.transaction_id == 123 }
24
55
  Then { echo_request.xid == 123 }
25
56
  Then { echo_request.user_data.empty? }
57
+ Then { echo_request.user_data == '' }
58
+ Then { echo_request.body.empty? }
59
+ Then { echo_request.body == '' }
26
60
  Then { echo_request.to_binary == [1, 2, 0, 8, 0, 0, 0, 123].pack('C*') }
27
61
  end
28
62
 
29
- context 'with 2**32' do
30
- When(:result) { Pio::Echo::Request.new(2**32) }
31
-
32
- Then { result == Failure(ArgumentError) }
33
- end
34
-
35
63
  context 'with transaction_id: 123' do
36
64
  When(:echo_request) { Pio::Echo::Request.new(transaction_id: 123) }
37
65
 
@@ -41,6 +69,9 @@ describe Pio::Echo::Request do
41
69
  Then { echo_request.transaction_id == 123 }
42
70
  Then { echo_request.xid == 123 }
43
71
  Then { echo_request.user_data.empty? }
72
+ Then { echo_request.user_data == '' }
73
+ Then { echo_request.body.empty? }
74
+ Then { echo_request.body == '' }
44
75
  Then { echo_request.to_binary == [1, 2, 0, 8, 0, 0, 0, 123].pack('C*') }
45
76
  end
46
77
 
@@ -53,6 +84,9 @@ describe Pio::Echo::Request do
53
84
  Then { echo_request.transaction_id == 123 }
54
85
  Then { echo_request.xid == 123 }
55
86
  Then { echo_request.user_data.empty? }
87
+ Then { echo_request.user_data == '' }
88
+ Then { echo_request.body.empty? }
89
+ Then { echo_request.body == '' }
56
90
  Then { echo_request.to_binary == [1, 2, 0, 8, 0, 0, 0, 123].pack('C*') }
57
91
  end
58
92
 
@@ -67,12 +101,33 @@ describe Pio::Echo::Request do
67
101
  Then { echo_request.transaction_id == 123 }
68
102
  Then { echo_request.xid == 123 }
69
103
  Then { echo_request.user_data == 'foobar' }
104
+ Then { echo_request.body == 'foobar' }
70
105
  Then do
71
106
  echo_request.to_binary ==
72
107
  [1, 2, 0, 14, 0, 0, 0, 123, 102, 111, 111, 98, 97, 114].pack('C*')
73
108
  end
74
109
  end
75
110
 
111
+ context 'with -1' do
112
+ When(:result) { Pio::Echo::Request.new(-1) }
113
+
114
+ Then do
115
+ result ==
116
+ Failure(ArgumentError,
117
+ 'Transaction ID should be an unsigned 32-bit integer.')
118
+ end
119
+ end
120
+
121
+ context 'with 2**32' do
122
+ When(:result) { Pio::Echo::Request.new(2**32) }
123
+
124
+ Then do
125
+ result ==
126
+ Failure(ArgumentError,
127
+ 'Transaction ID should be an unsigned 32-bit integer.')
128
+ end
129
+ end
130
+
76
131
  context 'with :INVALID_ARGUMENT' do
77
132
  When(:result) { Pio::Echo::Request.new(:INVALID_ARGUMENT) }
78
133
 
@@ -1,6 +1,83 @@
1
1
  require 'pio/features'
2
2
 
3
3
  describe Pio::Features::Reply do
4
+ context 'with a Features Reply message' do
5
+ When(:result) { Pio::Features::Reply.read(binary) }
6
+
7
+ describe '.read' do
8
+ Given(:binary) do
9
+ [0x01, 0x06, 0x00, 0xb0, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
10
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00,
11
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00,
12
+ 0x0f, 0xff, 0x00, 0x02, 0x16, 0x7d, 0xa4, 0x37, 0xba, 0x10,
13
+ 0x74, 0x72, 0x65, 0x6d, 0x61, 0x30, 0x2d, 0x30, 0x00, 0x00,
14
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
15
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00,
16
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
17
+ 0xff, 0xfe, 0x2a, 0xb4, 0xd6, 0x3c, 0x66, 0xba, 0x76, 0x73,
18
+ 0x77, 0x5f, 0x30, 0x78, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00,
19
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
20
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00,
21
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
22
+ 0x62, 0x94, 0x3a, 0xf6, 0x40, 0xdb, 0x74, 0x72, 0x65, 0x6d,
23
+ 0x61, 0x31, 0x2d, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
24
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
25
+ 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
26
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00].pack('C*')
27
+ end
28
+
29
+ Then { result.class == Pio::Features::Reply }
30
+ Then { result.ofp_version == 1 }
31
+ Then do
32
+ result.message_type ==
33
+ Pio::OpenFlow::Type::FEATURES_REPLY
34
+ end
35
+ Then { result.message_length == 176 }
36
+ Then { result.transaction_id == 2 }
37
+ Then { result.xid == 2 }
38
+ Then { !result.body.empty? }
39
+ Then { result.datapath_id == 1 }
40
+ Then { result.n_buffers == 0x100 }
41
+ Then { result.n_tables == 1 }
42
+ Then do
43
+ result.capabilities ==
44
+ [:flow_stats, :table_stats, :port_stats, :arp_match_ip]
45
+ end
46
+ Then do
47
+ result.actions ==
48
+ [:output, :set_vlan_vid, :set_vlan_pcp, :strip_vlan,
49
+ :set_dl_src, :set_dl_dst, :set_nw_src, :set_nw_dst,
50
+ :set_nw_tos, :set_tp_src, :set_tp_dst, :enqueue]
51
+ end
52
+ Then { result.ports.size == 3 }
53
+ Then { result.ports.all? { |each| each.port_no > 0 } }
54
+ Then do
55
+ result.ports.all? do |each|
56
+ /^([0-9a-fA-F]{2}[:-]){5}[0-9a-fA-F]{2}$/i=~
57
+ each.hardware_address.to_s
58
+ end
59
+ end
60
+ Then { result.ports.all? { |each| !each.name.empty? } }
61
+ Then { result.ports.any? { |each| each.config == [:port_down] } }
62
+ Then { result.ports.any? { |each| each.state == [:link_down] } }
63
+ Then { result.ports.all? { |each| each.curr.include? :port_copper } }
64
+ Then { result.ports.all? { |each| each.advertised.empty? } }
65
+ Then { result.ports.all? { |each| each.supported.empty? } }
66
+ Then { result.ports.all? { |each| each.peer.empty? } }
67
+ end
68
+
69
+ context 'with a Hello message' do
70
+ Given(:binary) { [1, 0, 0, 8, 0, 0, 0, 0].pack('C*') }
71
+
72
+ When(:result) { Pio::Features::Reply.read(binary) }
73
+
74
+ Then do
75
+ result == Failure(Pio::ParseError,
76
+ 'Invalid Features Reply message.')
77
+ end
78
+ end
79
+ end
80
+
4
81
  Given(:options) do
5
82
  {
6
83
  dpid: 0x123,
@@ -50,11 +127,11 @@ describe Pio::Features::Reply do
50
127
  Then { features_reply.ports[0].advertised.empty? }
51
128
  Then { features_reply.ports[0].supported.empty? }
52
129
  Then { features_reply.ports[0].peer.empty? }
53
- end
54
130
 
55
- describe '#to_binary' do
56
- When(:binary) { Pio::Features::Reply.new(options).to_binary }
131
+ describe '#to_binary' do
132
+ When(:result) { Pio::Features::Reply.new(options).to_binary }
57
133
 
58
- Then { binary.length > 0 }
134
+ Then { result.length > 0 }
135
+ end
59
136
  end
60
137
  end
@@ -1,59 +1,106 @@
1
1
  require 'pio/features'
2
2
 
3
3
  describe Pio::Features::Request do
4
+ describe '.read' do
5
+ When(:result) { Pio::Features::Request.read(binary) }
6
+
7
+ context 'with a Features Request message' do
8
+ Given(:binary) { [1, 5, 0, 8, 0, 0, 0, 0].pack('C*') }
9
+
10
+ Then { result.class == Pio::Features::Request }
11
+ Then { result.ofp_version == 1 }
12
+ Then do
13
+ result.message_type ==
14
+ Pio::OpenFlow::Type::FEATURES_REQUEST
15
+ end
16
+ Then { result.message_length == 8 }
17
+ Then { result.transaction_id == 0 }
18
+ Then { result.xid == 0 }
19
+ Then { result.body.empty? }
20
+ Then { result.body == '' }
21
+ end
22
+
23
+ context 'with a Hello message' do
24
+ Given(:binary) { [1, 0, 0, 8, 0, 0, 0, 0].pack('C*') }
25
+
26
+ Then do
27
+ result == Failure(Pio::ParseError,
28
+ 'Invalid Features Request message.')
29
+ end
30
+ end
31
+ end
32
+
4
33
  describe '.new' do
5
34
  context 'with no arguments' do
6
- When(:request) { Pio::Features::Request.new }
7
-
8
- Then { request.ofp_version == 1 }
9
- Then { request.message_type == Pio::OpenFlow::Type::FEATURES_REQUEST }
10
- Then { request.message_length == 8 }
11
- Then { request.transaction_id == 0 }
12
- Then { request.xid == 0 }
13
- Then { request.body.empty? }
14
- Then { request.to_binary == [1, 5, 0, 8, 0, 0, 0, 0].pack('C*') }
15
- end
35
+ When(:result) { Pio::Features::Request.new }
16
36
 
17
- context 'with 123' do
18
- When(:request) { Pio::Features::Request.new(123) }
19
-
20
- Then { request.ofp_version == 1 }
21
- Then { request.message_type == Pio::OpenFlow::Type::FEATURES_REQUEST }
22
- Then { request.message_length == 8 }
23
- Then { request.transaction_id == 123 }
24
- Then { request.xid == 123 }
25
- Then { request.body.empty? }
26
- Then { request.to_binary == [1, 5, 0, 8, 0, 0, 0, 123].pack('C*') }
37
+ Then { result.ofp_version == 1 }
38
+ Then { result.message_type == Pio::OpenFlow::Type::FEATURES_REQUEST }
39
+ Then { result.message_length == 8 }
40
+ Then { result.transaction_id == 0 }
41
+ Then { result.xid == 0 }
42
+ Then { result.body.empty? }
43
+ Then { result.body == '' }
44
+ Then { result.to_binary == [1, 5, 0, 8, 0, 0, 0, 0].pack('C*') }
27
45
  end
28
46
 
29
- context 'with 2**32' do
30
- When(:result) { Pio::Features::Request.new(2**32) }
47
+ context 'with 123' do
48
+ When(:result) { Pio::Features::Request.new(123) }
31
49
 
32
- Then { result == Failure(ArgumentError) }
50
+ Then { result.ofp_version == 1 }
51
+ Then { result.message_type == Pio::OpenFlow::Type::FEATURES_REQUEST }
52
+ Then { result.message_length == 8 }
53
+ Then { result.transaction_id == 123 }
54
+ Then { result.xid == 123 }
55
+ Then { result.body.empty? }
56
+ Then { result.body == '' }
57
+ Then { result.to_binary == [1, 5, 0, 8, 0, 0, 0, 123].pack('C*') }
33
58
  end
34
59
 
35
60
  context 'with transaction_id: 123' do
36
- When(:request) { Pio::Features::Request.new(transaction_id: 123) }
37
-
38
- Then { request.ofp_version == 1 }
39
- Then { request.message_type == Pio::OpenFlow::Type::FEATURES_REQUEST }
40
- Then { request.message_length == 8 }
41
- Then { request.transaction_id == 123 }
42
- Then { request.xid == 123 }
43
- Then { request.body.empty? }
44
- Then { request.to_binary == [1, 5, 0, 8, 0, 0, 0, 123].pack('C*') }
61
+ When(:result) { Pio::Features::Request.new(transaction_id: 123) }
62
+
63
+ Then { result.ofp_version == 1 }
64
+ Then { result.message_type == Pio::OpenFlow::Type::FEATURES_REQUEST }
65
+ Then { result.message_length == 8 }
66
+ Then { result.transaction_id == 123 }
67
+ Then { result.xid == 123 }
68
+ Then { result.body.empty? }
69
+ Then { result.body == '' }
70
+ Then { result.to_binary == [1, 5, 0, 8, 0, 0, 0, 123].pack('C*') }
45
71
  end
46
72
 
47
73
  context 'with xid: 123' do
48
- When(:request) { Pio::Features::Request.new(xid: 123) }
49
-
50
- Then { request.ofp_version == 1 }
51
- Then { request.message_type == Pio::OpenFlow::Type::FEATURES_REQUEST }
52
- Then { request.message_length == 8 }
53
- Then { request.transaction_id == 123 }
54
- Then { request.xid == 123 }
55
- Then { request.body.empty? }
56
- Then { request.to_binary == [1, 5, 0, 8, 0, 0, 0, 123].pack('C*') }
74
+ When(:result) { Pio::Features::Request.new(xid: 123) }
75
+
76
+ Then { result.ofp_version == 1 }
77
+ Then { result.message_type == Pio::OpenFlow::Type::FEATURES_REQUEST }
78
+ Then { result.message_length == 8 }
79
+ Then { result.transaction_id == 123 }
80
+ Then { result.xid == 123 }
81
+ Then { result.body.empty? }
82
+ Then { result.body == '' }
83
+ Then { result.to_binary == [1, 5, 0, 8, 0, 0, 0, 123].pack('C*') }
84
+ end
85
+
86
+ context 'with -1' do
87
+ When(:result) { Pio::Features::Request.new(-1) }
88
+
89
+ Then do
90
+ result ==
91
+ Failure(ArgumentError,
92
+ 'Transaction ID should be an unsigned 32-bit integer.')
93
+ end
94
+ end
95
+
96
+ context 'with 2**32' do
97
+ When(:result) { Pio::Features::Request.new(2**32) }
98
+
99
+ Then do
100
+ result ==
101
+ Failure(ArgumentError,
102
+ 'Transaction ID should be an unsigned 32-bit integer.')
103
+ end
57
104
  end
58
105
 
59
106
  context 'with :INVALID_ARGUMENT' do