bgp4r 0.0.11 → 0.0.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/bgp/common.rb +14 -13
  2. data/bgp/iana.rb +26 -1
  3. data/bgp/messages/capability.rb +3 -2
  4. data/bgp/messages/message.rb +3 -2
  5. data/bgp/messages/open.rb +2 -1
  6. data/bgp/messages/update.rb +80 -30
  7. data/bgp/neighbor/add_path_cap.rb +125 -0
  8. data/bgp/{neighbor.rb → neighbor/neighbor.rb} +64 -68
  9. data/bgp/nlris/nlri.rb +289 -15
  10. data/bgp/nlris/prefix.rb +3 -2
  11. data/bgp/nlris/vpn.rb +1 -8
  12. data/bgp/optional_parameters/add_path.rb +160 -0
  13. data/bgp/optional_parameters/capabilities.rb +1 -0
  14. data/bgp/optional_parameters/capability.rb +6 -0
  15. data/bgp/optional_parameters/graceful_restart.rb +6 -6
  16. data/bgp/optional_parameters/optional_parameter.rb +1 -0
  17. data/bgp/path_attributes/as_path.rb +1 -1
  18. data/bgp/path_attributes/attribute.rb +12 -5
  19. data/bgp/path_attributes/mp_reach.rb +142 -96
  20. data/bgp/path_attributes/mp_unreach.rb +73 -20
  21. data/bgp/path_attributes/path_attribute.rb +28 -5
  22. data/bgp4r.gemspec +21 -6
  23. data/bgp4r.rb +1 -1
  24. data/examples/unit-testing/malformed_update.rb +2 -1
  25. data/examples/unit-testing/test.rb +82 -0
  26. data/examples/unit-testing/test1.rb +82 -0
  27. data/examples/unit-testing/test2.rb +44 -0
  28. data/test/common_test.rb +7 -0
  29. data/test/helpers/server.rb +20 -0
  30. data/test/iana_test.rb +43 -0
  31. data/test/messages/open_test.rb +7 -2
  32. data/test/messages/update_test.rb +133 -36
  33. data/test/neighbor/add_path_cap_test.rb +54 -0
  34. data/test/neighbor/neighbor_test.rb +161 -0
  35. data/test/nlris/ext_nlri_test.rb +25 -60
  36. data/test/nlris/nlri_test.rb +93 -115
  37. data/test/optional_parameters/add_path_test.rb +53 -0
  38. data/test/optional_parameters/capability_test.rb +10 -0
  39. data/test/optional_parameters/graceful_restart_test.rb +1 -0
  40. data/test/path_attributes/mp_reach_test.rb +206 -8
  41. data/test/path_attributes/mp_unreach_test.rb +113 -5
  42. data/test/path_attributes/path_attribute_test.rb +34 -2
  43. metadata +20 -7
  44. data/test/neighbor_test.rb +0 -62
@@ -0,0 +1,160 @@
1
+ #--
2
+ # Copyright 2010 Jean-Michel Esnault.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #
6
+ #
7
+ # This file is part of BGP4R.
8
+ #
9
+ # BGP4R is free software: you can redistribute it and/or modify
10
+ # it under the terms of the GNU General Public License as published by
11
+ # the Free Software Foundation, either version 3 of the License, or
12
+ # (at your option) any later version.
13
+ #
14
+ # BGP4R is distributed in the hope that it will be useful,
15
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
+ # GNU General Public License for more details.
18
+ #
19
+ # You should have received a copy of the GNU General Public License
20
+ # along with BGP4R. If not, see <http://www.gnu.org/licenses/>.
21
+ #++
22
+
23
+ require 'bgp/optional_parameters/capability'
24
+
25
+ module BGP::OPT_PARM::CAP
26
+
27
+ #
28
+ # Belongs to Neighbor this is a session attribute....
29
+ # def include_path_id?(speeker, peer, sr, afi, safi)
30
+ # # when sending afi, safi, speaker agrees to send and peer to receive
31
+ # # when receiving afi, safi, speaker agrees to recv and peer agress to send
32
+ # case sr
33
+ # when :recv
34
+ # speeker.has?(:recv, afi, safi) && peer.has?(:send, afi, safi)
35
+ # when :send
36
+ # speeker.has?(:send, afi, safi) && peer.has?(:recv, afi, safi)
37
+ # end
38
+ # end
39
+
40
+ class Add_path < BGP::OPT_PARM::Capability
41
+
42
+ class << self
43
+ def new_array(arg)
44
+ o = new
45
+ arg.each { |t| o.add(*t) }
46
+ o
47
+ end
48
+ end
49
+
50
+ def initialize(*args)
51
+ @af={}
52
+ if args.size>1
53
+ super(OPT_PARM::CAP_ADD_PATH)
54
+ add(*args)
55
+ elsif args.size==1 and args[0].is_a?(String)
56
+ parse(*args)
57
+ elsif args.empty?
58
+ super(OPT_PARM::CAP_ADD_PATH)
59
+ else
60
+ raise
61
+ end
62
+ end
63
+
64
+ def add(sr, afi,safi)
65
+ @af[[_afi(afi), _safi(safi)]] = _send_recv(sr)
66
+ end
67
+
68
+ def parse(s)
69
+ families = super(s)
70
+ while families.size>0
71
+ afi, safi, sr = families.slice!(0,4).unpack('nCC')
72
+ @af[[afi,safi]]=sr
73
+ end
74
+ end
75
+
76
+ # 0001 01 01
77
+ # 0002 80 02
78
+ # 0002 01 03
79
+
80
+ def send? afi, safi
81
+ has? :send, afi, safi
82
+ end
83
+
84
+ def recv? afi, safi
85
+ has? :recv, afi, safi
86
+ end
87
+
88
+ def has?(sr, afi, safi)
89
+ case sr
90
+ when :recv
91
+ @af.has_key?([afi,safi]) && (2..3) === @af[[afi,safi]]
92
+ when :send
93
+ @af.has_key?([afi,safi]) && (@af[[afi,safi]] == 1 or @af[[afi,safi]] ==3)
94
+ end
95
+ end
96
+
97
+ def encode
98
+ s = []
99
+ s << @af.to_a.sort.collect { |e| e.flatten.pack('nCC') }
100
+ super s.join
101
+ end
102
+
103
+ def to_s
104
+ s = []
105
+ s << "\n Add-path Extension (#{CAP_ADD_PATH}), length: #{encode.size}"
106
+ s = s.join("\n ")
107
+ super + (s + (['']+@af.to_a.collect { |e| address_family_to_s(*e)}).join("\n "))
108
+ end
109
+
110
+ private
111
+
112
+ def address_family_to_s(af, sr)
113
+ afi, safi = af
114
+ "AFI #{IANA.afi(afi)} (#{afi}), SAFI #{IANA.safi(safi)} (#{safi}), #{send_recv_to_s(sr)}"
115
+ end
116
+
117
+ def _send_recv(val)
118
+ case val
119
+ when :send, 1 ; 1
120
+ when :recv, :receive, 2 ; 2
121
+ when :send_and_recv, :send_recv, :send_and_receive, 3 ; 3
122
+ else
123
+ val
124
+ end
125
+ end
126
+
127
+ def send_recv_to_s(val)
128
+ case val
129
+ when 1 ; 'SEND (1)'
130
+ when 2 ; 'RECV (2)'
131
+ when 3 ; 'SEND_AND_RECV (3)'
132
+ else
133
+ 'bogus'
134
+ end
135
+ end
136
+
137
+ def _afi(val)
138
+ if val.is_a?(Fixnum)
139
+ val
140
+ elsif val == :ipv4
141
+ 1
142
+ elsif val == :ipv6
143
+ 2
144
+ end
145
+ end
146
+
147
+ def _safi(val)
148
+ if val.is_a?(Fixnum)
149
+ val
150
+ elsif val == :unicast
151
+ 1
152
+ elsif val == :multicast
153
+ 2
154
+ end
155
+ end
156
+
157
+ end
158
+ end
159
+
160
+ load "../../test/optional_parameters/#{ File.basename($0.gsub(/.rb/,'_test.rb'))}" if __FILE__ == $0
@@ -7,6 +7,7 @@ require 'bgp/optional_parameters/optional_parameter'
7
7
  require 'bgp/optional_parameters/orf'
8
8
  require 'bgp/optional_parameters/route_refresh'
9
9
  require 'bgp/optional_parameters/as4'
10
+ require 'bgp/optional_parameters/add_path'
10
11
 
11
12
  module BGP::OPT_PARM
12
13
  module DYN_CAP
@@ -114,6 +114,8 @@ module BGP::OPT_PARM
114
114
  CAP::Orf.new(s)
115
115
  when CAP_GR
116
116
  CAP::Graceful_restart.new(s)
117
+ when CAP_ADD_PATH
118
+ CAP::Add_path.new(s)
117
119
  when CAP_DYNAMIC
118
120
  CAP::Dynamic.new(s)
119
121
  else
@@ -134,8 +136,12 @@ module BGP::OPT_PARM
134
136
  DYN_CAP::Route_refresh.new(code)
135
137
  when CAP_ORF,CAP_ORF_CISCO
136
138
  DYN_CAP::Orf.new(s)
139
+ when CAP_ADD_PATH
140
+ DYN_CAP::Add_path.new(s)
137
141
  when CAP_GR
138
142
  DYN_CAP::Graceful_restart.new(s)
143
+ when CAP_ADD_PATH
144
+ CAP::Add_path.new(s)
139
145
  else
140
146
  Capability::Unknown.new(s)
141
147
  end
@@ -29,7 +29,7 @@ class Graceful_restart < BGP::OPT_PARM::Capability
29
29
  def initialize(*args)
30
30
  if args.size>1
31
31
  @restart_state, @restart_time = args
32
- @address_families = []
32
+ @tuples = []
33
33
  super(OPT_PARM::CAP_GR)
34
34
  else
35
35
  parse(*args)
@@ -37,16 +37,16 @@ class Graceful_restart < BGP::OPT_PARM::Capability
37
37
  end
38
38
 
39
39
  def add(afi,safi,flags)
40
- @address_families << [_afi(afi), _safi(safi), flags]
40
+ @tuples << [_afi(afi), _safi(safi), flags]
41
41
  end
42
42
 
43
43
  def parse(s)
44
- @address_families = []
44
+ @tuples = []
45
45
  o1, families = super(s).unpack('na*')
46
46
  @restart_state = o1 >> 12
47
47
  @restart_time = o1 & 0xfff
48
48
  while families.size>0
49
- @address_families << families.slice!(0,4).unpack('nCC')
49
+ @tuples << families.slice!(0,4).unpack('nCC')
50
50
  end
51
51
  end
52
52
 
@@ -54,7 +54,7 @@ class Graceful_restart < BGP::OPT_PARM::Capability
54
54
  def encode
55
55
  s = []
56
56
  s << [(@restart_state << 12) + @restart_time].pack('n')
57
- s << @address_families.collect { |af| af.pack('nCC') }
57
+ s << @tuples.collect { |af| af.pack('nCC') }
58
58
  super s.join
59
59
  end
60
60
  def to_s
@@ -62,7 +62,7 @@ class Graceful_restart < BGP::OPT_PARM::Capability
62
62
  s << "\n Graceful Restart Extension (#{CAP_GR}), length: 4"
63
63
  s << " Restart Flags: #{restart_flag}, Restart Time #{@restart_time}s"
64
64
  s = s.join("\n ")
65
- super + (s + (['']+@address_families.collect { |af| address_family(*af)}).join("\n "))
65
+ super + (s + (['']+@tuples.collect { |af| address_family(*af)}).join("\n "))
66
66
  end
67
67
 
68
68
  private
@@ -36,6 +36,7 @@ module OPT_PARM
36
36
  CAP_GR = 64
37
37
  CAP_AS4 = 65
38
38
  CAP_DYNAMIC = 67
39
+ CAP_ADD_PATH = 69
39
40
  CAP_ROUTE_REFRESH_CISCO = 128
40
41
  CAP_ORF_CISCO = 130
41
42
 
@@ -156,7 +156,7 @@ module BGP
156
156
  attr_accessor :as4byte
157
157
 
158
158
  def initialize(*args)
159
-
159
+
160
160
  @flags, @type, @segments, @as4byte = WELL_KNOWN_MANDATORY, AS_PATH, [], false
161
161
 
162
162
  if args[0].is_a?(String) and args[0].is_packed?
@@ -61,16 +61,23 @@ module BGP
61
61
  OPTIONAL_TRANSITIVE = OPTIONAL | TRANSITIVE
62
62
  OPTIONAL_NON_TRANSITIVE = OPTIONAL
63
63
 
64
- def encode(value='',value_fmt=nil)
65
- len, len_fmt = value.size, 'C'
64
+ def len_fmt(len)
66
65
  if len>255
67
66
  @flags |= EXTENDED_LENGTH
68
- len_fmt='n'
67
+ @_len_fmt='n'
68
+ else
69
+ @_len_fmt = 'C'
70
+ @flags &= ~EXTENDED_LENGTH
69
71
  end
72
+ end
73
+
74
+ def encode(value='',value_fmt=nil)
75
+ len = value.size
76
+ len_fmt(len)
70
77
  if value_fmt
71
- [@flags<<4, @type, len, *value].pack("CC#{len_fmt}#{value_fmt}")
78
+ [@flags<<4, @type, len, *value].pack("CC#{@_len_fmt}#{value_fmt}")
72
79
  else
73
- ([@flags<<4, @type, len].pack("CC#{len_fmt}") + value).is_packed
80
+ ([@flags<<4, @type, len].pack("CC#{@_len_fmt}") + value).is_packed
74
81
  end
75
82
  end
76
83
 
@@ -1,4 +1,4 @@
1
- #--
1
+ #--
2
2
  # Copyright 2008, 2009 Jean-Michel Esnault.
3
3
  # All rights reserved.
4
4
  # See LICENSE.txt for permissions.
@@ -24,119 +24,165 @@ require 'bgp/path_attributes/attribute'
24
24
  require 'bgp/nlris/nlris'
25
25
 
26
26
  module BGP
27
-
28
- class Mp_reach < Attr
29
-
30
- attr_reader :safi, :nlris
31
-
32
- def initialize(*args)
33
- @safi, @nexthops, @nlris= 1, [], [] # default is ipv4/unicast
34
- @flags, @type = OPTIONAL, MP_REACH
35
- if args[0].is_a?(String) and args[0].is_packed?
36
- parse(args[0])
37
- elsif args[0].is_a?(self.class)
38
- parse(args[0].encode, *args[1..-1])
39
- elsif args[0].is_a?(Hash) and args.size==1
40
- set(*args)
41
- else
42
- raise ArgumentError, "invalid argument"
43
- end
27
+
28
+ class Mp_reach < Attr
29
+
30
+ attr_reader :safi, :nlris, :path_id
31
+
32
+ def initialize(*args)
33
+ @safi, @nexthops, @nlris, @path_id= 1, [], [], nil # default is ipv4/unicast
34
+ @flags, @type = OPTIONAL, MP_REACH
35
+ if args[0].is_a?(String) and args[0].is_packed?
36
+ parse(*args)
37
+ elsif args[0].is_a?(self.class)
38
+ s = args.shift.encode
39
+ parse(s, *args)
40
+ elsif args[0].is_a?(Hash) and args.size==1
41
+ set(*args)
42
+ else
43
+ raise ArgumentError, "invalid argument"
44
44
  end
45
-
46
- def afi
47
- @afi ||= @nexthops[0].afi
45
+ end
46
+
47
+ def afi
48
+ @afi ||= @nexthops[0].afi
49
+ end
50
+
51
+ def set(h)
52
+ @safi = h[:safi]
53
+ @path_id = path_id = h[:path_id]
54
+ case @safi
55
+ when 1,2,4 ; @nexthops = [h[:nexthop]].flatten.collect { |nh| Prefix.new(nh) }
56
+ when 128,129 ; @nexthops = [h[:nexthop]].flatten.collect { |nh| Vpn.new(nh) }
57
+ else
48
58
  end
49
-
50
- def set(h)
51
- @safi = h[:safi]
52
- case @safi
53
- when 1,2,4 ; @nexthops = [h[:nexthop]].flatten.collect { |nh| Prefix.new(nh) }
54
- when 128,129 ; @nexthops = [h[:nexthop]].flatten.collect { |nh| Vpn.new(nh) }
55
- else
59
+ case @safi
60
+ when 1
61
+ @nlris = [h[:nlris]].flatten.collect do |n|
62
+ case n
63
+ when String
64
+ nlri = Inet_unicast.new(n)
65
+ path_id ? Ext_Nlri.new(path_id, nlri) : nlri
66
+ when Hash
67
+ path_id = n[:path_id] if n[:path_id]
68
+ nlri = Inet_unicast.new(n[:prefix])
69
+ path_id ? Ext_Nlri.new(path_id, nlri) : nlri
70
+ else
71
+ raise ArgumentError, "Invalid: #{n.inspect}"
72
+ end
56
73
  end
57
- case @safi
58
- when 1,2
59
- @nlris = [h[:prefix]].flatten.collect { |n| Prefix.new(n) }
60
- when 4
61
- @nlris = [h[:nlri]].flatten.collect { |n|
62
- prefix = n[:prefix].is_a?(String) ? Prefix.new(n[:prefix]) : n[:prefix]
63
- Labeled.new(prefix, *n[:label])
64
- }
65
- when 128,129 ; @nlris = [h[:nlri]].flatten.collect { |n|
74
+ when 2
75
+ @nlris = [h[:nlris]].flatten.collect do |n|
76
+ case n
77
+ when String
78
+ nlri = Inet_multicast.new(n)
79
+ path_id ? Ext_Nlri.new(path_id, nlri) : nlri
80
+ when Hash
81
+ path_id = n[:path_id] if n[:path_id]
82
+ nlri = Inet_multicast.new(n[:prefix])
83
+ path_id ? Ext_Nlri.new(path_id, nlri) : nlri
84
+ else
85
+ raise ArgumentError, "Invalid: #{n.inspect}"
86
+ end
87
+ end
88
+ when 4
89
+ @nlris = [h[:nlris]].flatten.collect do |n|
90
+ path_id = n[:path_id] || @path_id
91
+ prefix = n[:prefix].is_a?(String) ? Prefix.new(n[:prefix]) : n[:prefix]
92
+ nlri = Labeled.new(prefix, *n[:label])
93
+ path_id ? Ext_Nlri.new(path_id, nlri) : nlri
94
+ end
95
+ when 128,129
96
+ @nlris = [h[:nlris]].flatten.collect do |n|
97
+ path_id = n[:path_id] || @path_id
66
98
  prefix = n[:prefix].is_a?(Prefix) ? n[:prefix] : Prefix.new(n[:prefix])
67
99
  rd = n[:rd].is_a?(Rd) ? n[:rd] : Rd.new(*n[:rd])
68
- Labeled.new(Vpn.new(prefix,rd), *n[:label]) }
69
- else
100
+ nlri = Labeled.new(Vpn.new(prefix,rd), *n[:label])
101
+ path_id ? Ext_Nlri.new(path_id, nlri) : nlri
70
102
  end
103
+ else
71
104
  end
105
+ end
106
+
107
+ def nexthops
108
+ @nexthops.collect { |nh| nh.nexthop }.join(", ")
109
+ end
110
+
111
+ def mp_reach
112
+ "\n AFI #{IANA.afi(afi)} (#{afi}), SAFI #{IANA.safi(safi)} (#{safi})" +
113
+ "\n nexthop: " + nexthops +
114
+ (['']+ @nlris.collect { |nlri| nlri.to_s }).join("\n ")
115
+ end
116
+
117
+ def to_s(method=:default)
118
+ super(mp_reach, method)
119
+ end
120
+
121
+ def to_hash
122
+ end
123
+
124
+ def parse(s,arg=false)
72
125
 
73
- def nexthops
74
- @nexthops.collect { |nh| nh.nexthop }.join(", ")
75
- end
76
-
77
- def mp_reach
78
- "\n AFI #{IANA.afi(afi)} (#{afi}), SAFI #{IANA.safi(safi)} (#{safi})" +
79
- "\n nexthop: " + nexthops +
80
- (['']+ @nlris.collect { |nlri| nlri.to_s }).join("\n ")
81
- end
82
-
83
- def to_s(method=:default)
84
- super(mp_reach, method)
85
- end
126
+ @flags, @type, len, value = super(s)
127
+ @afi, @safi, nh_len = value.slice!(0,4).unpack('nCC')
128
+ parse_next_hops value.slice!(0,nh_len).is_packed
129
+ value.slice!(0,1)
86
130
 
87
- def to_hash
131
+ if arg.respond_to?(:path_id?)
132
+ path_id_flag = arg.path_id? :recv, @afi, @safi
133
+ else
134
+ path_id_flag = arg
88
135
  end
89
136
 
90
-
91
- def parse(s)
92
- @flags, @type, len, value = super(s)
93
- @afi, @safi, nh_len = value.slice!(0,4).unpack('nCC')
94
- parse_next_hops value.slice!(0,nh_len).is_packed
95
- value.slice!(0,1)
96
- while value.size>0
97
- blen = value.slice(0,1).unpack('C')[0]
98
- @nlris << Nlri.factory(value.slice!(0,(blen+7)/8+1), @afi, @safi)
137
+ while value.size>0
138
+ path_id = value.slice!(0,4).unpack('N')[0] if path_id_flag
139
+ blen = value.slice(0,1).unpack('C')[0]
140
+ nlri = Nlri.factory(value.slice!(0,(blen+7)/8+1), @afi, @safi)
141
+ if path_id_flag
142
+ @nlris << Ext_Nlri.new(path_id, nlri)
143
+ else
144
+ @nlris << nlri
99
145
  end
100
- raise RuntimeError, "leftover afer parsing: #{value.unpack('H*')}" if value.size>0
101
-
102
146
  end
103
-
104
- def parse_next_hops(s)
105
- while s.size>0
106
- case @safi
107
- when 1,2,4
108
- case @afi
109
- when 1
110
- @nexthops << Prefix.new([32,s.slice!(0,4)].pack('Ca*'),1)
111
- when 2
112
- @nexthops << Prefix.new([128,s.slice!(0,16)].pack('Ca*'),2)
113
- end
114
- when 128,129
115
- @nexthops << Vpn.new([64+32,s.slice!(0,12)].pack('Ca*'))
116
- else
117
- raise RuntimeError, "cannot parse nexthop for safi #{@safi}"
147
+ raise RuntimeError, "leftover afer parsing: #{value.unpack('H*')}" if value.size>0
148
+ end
149
+
150
+ def parse_next_hops(s)
151
+ while s.size>0
152
+ case @safi
153
+ when 1,2,4
154
+ case @afi
155
+ when 1
156
+ @nexthops << Prefix.new([32,s.slice!(0,4)].pack('Ca*'),1)
157
+ when 2
158
+ @nexthops << Prefix.new([128,s.slice!(0,16)].pack('Ca*'),2)
118
159
  end
160
+ when 128,129
161
+ @nexthops << Vpn.new([64+32,s.slice!(0,12)].pack('Ca*'))
162
+ else
163
+ raise RuntimeError, "cannot parse nexthop for safi #{@safi}"
119
164
  end
120
165
  end
121
-
122
- def encode(what=:mp_reach)
123
- case what
124
- when :mp_reach
125
- nexthops = @nexthops.collect { |nh| nh.encode(false) }.join
126
- super([afi, @safi, nexthops.size, nexthops, 0, @nlris.collect { |n| n.encode }.join].pack('nCCa*Ca*'))
127
- when :mp_unreach
128
- super([afi, @safi, @nlris.collect { |n| n.encode }.join].pack('nCa*'))
129
- end
130
- end
131
-
132
- def new_unreach
133
- s = encode(:mp_unreach)
134
- s[1]= [MP_UNREACH].pack('C')
135
- Mp_unreach.new(s)
166
+ end
167
+
168
+ def encode(what=:mp_reach)
169
+ case what
170
+ when :mp_reach
171
+ nexthops = @nexthops.collect { |nh| nh.encode(false) }.join
172
+ super([afi, @safi, nexthops.size, nexthops, 0, @nlris.collect { |n| n.encode }.join].pack('nCCa*Ca*'))
173
+ when :mp_unreach
174
+ super([afi, @safi, @nlris.collect { |n| n.encode }.join].pack('nCa*'))
136
175
  end
137
-
138
176
  end
139
-
177
+
178
+ def new_unreach
179
+ s = encode(:mp_unreach)
180
+ s[1]= [MP_UNREACH].pack('C')
181
+ Mp_unreach.new(s)
182
+ end
183
+
184
+ end
185
+
140
186
  end
141
187
 
142
188
  load "../../test/path_attributes/#{ File.basename($0.gsub(/.rb/,'_test.rb'))}" if __FILE__ == $0