rspec-puppet 0.1.6 → 1.0.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.
- checksums.yaml +7 -0
- data/README.md +71 -6
- data/lib/rspec-puppet.rb +45 -0
- data/lib/rspec-puppet/errors.rb +83 -0
- data/lib/rspec-puppet/example/class_example_group.rb +1 -55
- data/lib/rspec-puppet/example/define_example_group.rb +1 -57
- data/lib/rspec-puppet/example/function_example_group.rb +21 -35
- data/lib/rspec-puppet/example/host_example_group.rb +1 -26
- data/lib/rspec-puppet/matchers.rb +3 -1
- data/lib/rspec-puppet/matchers/compile.rb +133 -0
- data/lib/rspec-puppet/matchers/count_generic.rb +73 -0
- data/lib/rspec-puppet/matchers/create_generic.rb +155 -52
- data/lib/rspec-puppet/matchers/dynamic_matchers.rb +17 -0
- data/lib/rspec-puppet/matchers/include_class.rb +2 -1
- data/lib/rspec-puppet/matchers/parameter_matcher.rb +110 -0
- data/lib/rspec-puppet/matchers/run.rb +49 -31
- data/lib/rspec-puppet/setup.rb +57 -34
- data/lib/rspec-puppet/support.rb +154 -7
- metadata +18 -32
- data/LICENSE +0 -20
- data/Rakefile +0 -7
- data/lib/rspec-puppet/matchers/create_resource.rb +0 -53
- data/rspec-puppet.gemspec +0 -47
- data/spec/classes/boolean_regexp_spec.rb +0 -10
- data/spec/classes/boolean_spec.rb +0 -11
- data/spec/classes/sysctl_common_spec.rb +0 -40
- data/spec/defines/sysctl_before_spec.rb +0 -26
- data/spec/defines/sysctl_spec.rb +0 -14
- data/spec/fixtures/manifests/site.pp +0 -7
- data/spec/fixtures/modules/boolean/manifests/init.pp +0 -12
- data/spec/fixtures/modules/sysctl/manifests/init.pp +0 -39
- data/spec/functions/split_spec.rb +0 -17
- data/spec/hosts/foo_spec.rb +0 -6
- data/spec/hosts/testhost_spec.rb +0 -5
- data/spec/spec_helper.rb +0 -6
@@ -4,32 +4,7 @@ module RSpec::Puppet
|
|
4
4
|
include RSpec::Puppet::Support
|
5
5
|
|
6
6
|
def subject
|
7
|
-
@catalogue ||= catalogue
|
8
|
-
end
|
9
|
-
|
10
|
-
def catalogue
|
11
|
-
vardir = Dir.mktmpdir
|
12
|
-
Puppet[:vardir] = vardir
|
13
|
-
Puppet[:hiera_config] = File.join(vardir, "hiera.yaml") if Puppet[:hiera_config] == File.expand_path("/dev/null")
|
14
|
-
Puppet[:modulepath] = self.respond_to?(:module_path) ? module_path : RSpec.configuration.module_path
|
15
|
-
Puppet[:manifestdir] = self.respond_to?(:manifest_dir) ? manifest_dir : RSpec.configuration.manifest_dir
|
16
|
-
Puppet[:manifest] = self.respond_to?(:manifest) ? manifest : RSpec.configuration.manifest
|
17
|
-
Puppet[:templatedir] = self.respond_to?(:template_dir) ? template_dir : RSpec.configuration.template_dir
|
18
|
-
Puppet[:config] = self.respond_to?(:config) ? config : RSpec.configuration.config
|
19
|
-
code = ""
|
20
|
-
|
21
|
-
nodename = self.class.top_level_description.downcase
|
22
|
-
|
23
|
-
facts_val = {
|
24
|
-
'hostname' => nodename.split('.').first,
|
25
|
-
'fqdn' => nodename,
|
26
|
-
'domain' => nodename.split('.').last,
|
27
|
-
}
|
28
|
-
facts_val.merge!(munge_facts(facts)) if self.respond_to?(:facts)
|
29
|
-
|
30
|
-
catalogue = build_catalog(nodename, facts_val, code)
|
31
|
-
FileUtils.rm_rf(vardir) if File.directory?(vardir)
|
32
|
-
catalogue
|
7
|
+
@catalogue ||= catalogue(:host)
|
33
8
|
end
|
34
9
|
end
|
35
10
|
end
|
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'rspec-puppet/matchers/create_generic'
|
2
|
-
require 'rspec-puppet/matchers/create_resource'
|
3
2
|
require 'rspec-puppet/matchers/include_class'
|
3
|
+
require 'rspec-puppet/matchers/compile'
|
4
4
|
require 'rspec-puppet/matchers/run'
|
5
|
+
require 'rspec-puppet/matchers/count_generic'
|
6
|
+
require 'rspec-puppet/matchers/dynamic_matchers'
|
@@ -0,0 +1,133 @@
|
|
1
|
+
module RSpec::Puppet
|
2
|
+
module ManifestMatchers
|
3
|
+
class Compile
|
4
|
+
def initialize
|
5
|
+
@failed_resource = ""
|
6
|
+
@check_deps = false
|
7
|
+
@cycles = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def with_all_deps
|
11
|
+
@check_deps = true
|
12
|
+
self
|
13
|
+
end
|
14
|
+
|
15
|
+
def matches?(catalogue)
|
16
|
+
@catalogue = catalogue
|
17
|
+
if cycles_found?
|
18
|
+
false
|
19
|
+
elsif @check_deps == true && missing_dependencies?
|
20
|
+
false
|
21
|
+
else
|
22
|
+
true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def description
|
27
|
+
"compile the catalogue without cycles"
|
28
|
+
end
|
29
|
+
|
30
|
+
def failure_message_for_should
|
31
|
+
unless @cycles.empty?
|
32
|
+
"dependency cycles found: #{@cycles.join('; ')}"
|
33
|
+
else
|
34
|
+
"expected that the catalogue would include #{@failed_resource}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def failure_message_for_should_not
|
39
|
+
"expected that the catalogue would not compile but it does"
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
def missing_dependencies?
|
44
|
+
retval = false
|
45
|
+
|
46
|
+
resource_vertices = @catalogue.vertices.select { |v| v.is_a? Puppet::Resource }
|
47
|
+
resource_vertices.each do |vertex|
|
48
|
+
vertex.each do |param,value|
|
49
|
+
if [:require, :subscribe, :notify, :before].include? param
|
50
|
+
value = Array[value] unless value.is_a? Array
|
51
|
+
value.each do |val|
|
52
|
+
if val.is_a? Puppet::Resource
|
53
|
+
retval = true unless resource_exists?(val, vertex)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
retval
|
61
|
+
end
|
62
|
+
|
63
|
+
def resource_hash
|
64
|
+
@resource_hash ||= Proc.new do
|
65
|
+
res_hash = {}
|
66
|
+
@catalogue.vertices.each do |vertex|
|
67
|
+
if vertex.is_a? Puppet::Resource
|
68
|
+
res_hash[vertex.ref] = 1
|
69
|
+
if vertex[:alias]
|
70
|
+
res_hash["#{vertex.type.to_s}[#{vertex[:alias]}]"] = 1
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
res_hash
|
75
|
+
end.call
|
76
|
+
end
|
77
|
+
|
78
|
+
def check_resource(res)
|
79
|
+
if resource_hash[res.ref]
|
80
|
+
true
|
81
|
+
elsif res[:alias] && resource_hash["#{res.type.to_s}[#{res[:alias]}]"]
|
82
|
+
true
|
83
|
+
else
|
84
|
+
false
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def resource_exists?(res, vertex)
|
89
|
+
unless check_resource(res)
|
90
|
+
@failed_resource = "#{res.ref} used at #{vertex.file}:#{vertex.line} in #{vertex.ref}"
|
91
|
+
false
|
92
|
+
else
|
93
|
+
true
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def cycles_found?
|
98
|
+
retval = false
|
99
|
+
begin
|
100
|
+
cat = @catalogue.to_ral.relationship_graph
|
101
|
+
cat.write_graph(:resources)
|
102
|
+
if cat.respond_to? :find_cycles_in_graph
|
103
|
+
find_cycles(cat)
|
104
|
+
else
|
105
|
+
find_cycles_legacy(cat)
|
106
|
+
end
|
107
|
+
retval = true unless @cycles.empty?
|
108
|
+
rescue Puppet::Error
|
109
|
+
retval = true
|
110
|
+
end
|
111
|
+
retval
|
112
|
+
end
|
113
|
+
|
114
|
+
def find_cycles(catalogue)
|
115
|
+
cycles = catalogue.find_cycles_in_graph
|
116
|
+
if cycles.length > 0
|
117
|
+
cycles.each do |cycle|
|
118
|
+
paths = catalogue.paths_in_cycle(cycle)
|
119
|
+
@cycles << (paths.map{ |path| '(' + path.join(" => ") + ')'}.join("\n") + "\n")
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def find_cycles_legacy(catalogue)
|
125
|
+
begin
|
126
|
+
catalogue.topsort
|
127
|
+
rescue Puppet::Error => e
|
128
|
+
@cycles = [e.message.rpartition(';').first.partition(':').last]
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module RSpec::Puppet
|
2
|
+
module ManifestMatchers
|
3
|
+
class CountGeneric
|
4
|
+
def initialize(type, count, *method)
|
5
|
+
if type.nil?
|
6
|
+
@type = method[0].to_s.gsub(/^have_(.+)_resource_count$/, '\1')
|
7
|
+
else
|
8
|
+
@type = type
|
9
|
+
end
|
10
|
+
@referenced_type = referenced_type(@type)
|
11
|
+
@expected_number = count.to_i
|
12
|
+
end
|
13
|
+
|
14
|
+
def matches?(catalogue)
|
15
|
+
if @type == "resource"
|
16
|
+
@actual_number = catalogue.resources.count do |res|
|
17
|
+
!(['Class', 'Node'].include? res.type)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Puppet automatically adds Stage[main]
|
21
|
+
@actual_number = @actual_number - 1
|
22
|
+
else
|
23
|
+
@actual_number = catalogue.resources.count do |res|
|
24
|
+
res.type == @referenced_type
|
25
|
+
end
|
26
|
+
|
27
|
+
# Puppet automatically adds Class[main] and Class[Settings]
|
28
|
+
@actual_number = @actual_number - 2 if @type == "class"
|
29
|
+
end
|
30
|
+
|
31
|
+
@actual_number == @expected_number
|
32
|
+
end
|
33
|
+
|
34
|
+
def description
|
35
|
+
desc = []
|
36
|
+
|
37
|
+
desc << "contain exactly #{@expected_number}"
|
38
|
+
if @type == "class"
|
39
|
+
desc << "#{@expected_number == 1 ? "class" : "classes" }"
|
40
|
+
else
|
41
|
+
unless @type == "resource"
|
42
|
+
desc << "#{@referenced_type}"
|
43
|
+
end
|
44
|
+
desc << "#{@expected_number == 1 ? "resource" : "resources" }"
|
45
|
+
end
|
46
|
+
|
47
|
+
desc.join(" ")
|
48
|
+
end
|
49
|
+
|
50
|
+
def failure_message_for_should
|
51
|
+
"expected that the catalogue would " + description + " but it contains #{@actual_number}"
|
52
|
+
end
|
53
|
+
|
54
|
+
def failure_message_for_should_not
|
55
|
+
"expected that the catalogue would not " + description + " but it does"
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def referenced_type(type)
|
61
|
+
type.split('__').map { |r| r.capitalize }.join('::')
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def have_class_count(count)
|
66
|
+
RSpec::Puppet::ManifestMatchers::CountGeneric.new('class', count)
|
67
|
+
end
|
68
|
+
|
69
|
+
def have_resource_count(count)
|
70
|
+
RSpec::Puppet::ManifestMatchers::CountGeneric.new('resource', count)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -1,34 +1,76 @@
|
|
1
|
+
require 'rspec-puppet/matchers/parameter_matcher'
|
1
2
|
module RSpec::Puppet
|
2
3
|
module ManifestMatchers
|
3
4
|
class CreateGeneric
|
5
|
+
include RSpec::Puppet::Errors
|
6
|
+
|
4
7
|
def initialize(*args, &block)
|
5
8
|
@exp_resource_type = args.shift.to_s.gsub(/^(create|contain)_/, '')
|
6
9
|
@args = args
|
7
10
|
@block = block
|
8
11
|
@referenced_type = referenced_type(@exp_resource_type)
|
9
12
|
@title = args[0]
|
13
|
+
|
14
|
+
@errors = []
|
15
|
+
@expected_params = []
|
16
|
+
@expected_undef_params = []
|
17
|
+
@notifies = []
|
18
|
+
@subscribes = []
|
19
|
+
@requires = []
|
20
|
+
@befores = []
|
10
21
|
end
|
11
22
|
|
12
23
|
def with(*args, &block)
|
13
24
|
params = args.shift
|
14
|
-
@expected_params =
|
25
|
+
@expected_params = @expected_params | params.to_a
|
15
26
|
self
|
16
27
|
end
|
17
28
|
|
29
|
+
def only_with(*args, &block)
|
30
|
+
params = args.shift
|
31
|
+
@expected_params_count = (@expected_params_count || 0) + params.size
|
32
|
+
self.with(params, &block)
|
33
|
+
end
|
34
|
+
|
18
35
|
def without(*args, &block)
|
19
36
|
params = args.shift
|
20
|
-
@expected_undef_params =
|
37
|
+
@expected_undef_params = @expected_undef_params | Array(params)
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
def that_notifies(resource)
|
42
|
+
@notifies << resource
|
43
|
+
self
|
44
|
+
end
|
45
|
+
|
46
|
+
def that_subscribes_to(resource)
|
47
|
+
@subscribes << resource
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
51
|
+
def that_requires(resource)
|
52
|
+
@requires << resource
|
53
|
+
self
|
54
|
+
end
|
55
|
+
|
56
|
+
def that_comes_before(resource)
|
57
|
+
@befores << resource
|
21
58
|
self
|
22
59
|
end
|
23
60
|
|
24
61
|
def method_missing(method, *args, &block)
|
25
62
|
if method.to_s =~ /^with_/
|
26
63
|
param = method.to_s.gsub(/^with_/, '')
|
27
|
-
|
64
|
+
@expected_params << [param, args[0]]
|
65
|
+
self
|
66
|
+
elsif method.to_s =~ /^only_with_/
|
67
|
+
param = method.to_s.gsub(/^only_with_/, '')
|
68
|
+
@expected_params_count = (@expected_params_count || 0) + 1
|
69
|
+
@expected_params << [param, args[0]]
|
28
70
|
self
|
29
71
|
elsif method.to_s =~ /^without_/
|
30
72
|
param = method.to_s.gsub(/^without_/, '')
|
31
|
-
|
73
|
+
@expected_undef_params << [param, args[0]]
|
32
74
|
self
|
33
75
|
else
|
34
76
|
super
|
@@ -40,41 +82,25 @@ module RSpec::Puppet
|
|
40
82
|
resource = catalogue.resource(@referenced_type, @title)
|
41
83
|
|
42
84
|
if resource.nil?
|
43
|
-
|
85
|
+
false
|
44
86
|
else
|
45
87
|
rsrc_hsh = resource.to_hash
|
46
|
-
if @
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
ret = false
|
51
|
-
(@errors ||= []) << "#{name.to_s} matching `#{value.inspect}` but its value of `#{rsrc_hsh[name.to_sym].inspect}` does not"
|
52
|
-
end
|
53
|
-
elsif value.kind_of?(Array) then
|
54
|
-
unless Array(rsrc_hsh[name.to_sym]).flatten.join == value.flatten.join
|
55
|
-
ret = false
|
56
|
-
(@errors ||= []) << "#{name.to_s} set to `#{value.inspect}` but it is set to `#{rsrc_hsh[name.to_sym].inspect}` in the catalogue"
|
57
|
-
end
|
58
|
-
else
|
59
|
-
unless rsrc_hsh[name.to_sym].to_s == value.to_s
|
60
|
-
ret = false
|
61
|
-
(@errors ||= []) << "#{name.to_s} set to `#{value.inspect}` but it is set to `#{rsrc_hsh[name.to_sym].inspect}` in the catalogue"
|
62
|
-
end
|
63
|
-
end
|
88
|
+
if @expected_params_count
|
89
|
+
unless rsrc_hsh.size == @expected_params_count
|
90
|
+
ret = false
|
91
|
+
(@errors ||= []) << "exactly #{@expected_params_count} parameters but the catalogue contains #{rsrc_hsh.size}"
|
64
92
|
end
|
65
93
|
end
|
66
94
|
|
67
|
-
if @
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
95
|
+
check_params(rsrc_hsh, @expected_params, :should) if @expected_params.any?
|
96
|
+
check_params(rsrc_hsh, @expected_undef_params, :not) if @expected_undef_params.any?
|
97
|
+
check_befores(catalogue, resource) if @befores.any?
|
98
|
+
check_requires(catalogue, resource) if @requires.any?
|
99
|
+
check_notifies(catalogue, resource) if @notifies.any?
|
100
|
+
check_subscribes(catalogue, resource) if @subscribes.any?
|
76
101
|
|
77
|
-
|
102
|
+
@errors.empty?
|
103
|
+
end
|
78
104
|
end
|
79
105
|
|
80
106
|
def failure_message_for_should
|
@@ -87,20 +113,17 @@ module RSpec::Puppet
|
|
87
113
|
|
88
114
|
def description
|
89
115
|
values = []
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
values << "#{name.to_s} matching #{value.inspect}"
|
94
|
-
else
|
95
|
-
values << "#{name.to_s} => #{value.inspect}"
|
96
|
-
end
|
97
|
-
end
|
116
|
+
|
117
|
+
if @expected_params_count
|
118
|
+
values << "exactly #{@expected_params_count} parameters"
|
98
119
|
end
|
99
120
|
|
100
|
-
if @
|
101
|
-
@
|
102
|
-
|
103
|
-
|
121
|
+
if @expected_params.any?
|
122
|
+
values.concat(generate_param_list(@expected_params, :should))
|
123
|
+
end
|
124
|
+
|
125
|
+
if @expected_undef_params.any?
|
126
|
+
values.concat(generate_param_list(@expected_undef_params, :not))
|
104
127
|
end
|
105
128
|
|
106
129
|
unless values.empty?
|
@@ -114,20 +137,100 @@ module RSpec::Puppet
|
|
114
137
|
"contain #{@referenced_type}[#{@title}]#{value_str}"
|
115
138
|
end
|
116
139
|
|
117
|
-
|
118
|
-
|
140
|
+
private
|
119
141
|
def referenced_type(type)
|
120
142
|
type.split('__').map { |r| r.capitalize }.join('::')
|
121
143
|
end
|
122
144
|
|
123
145
|
def errors
|
124
|
-
@errors.
|
146
|
+
@errors.empty? ? "" : " with #{@errors.join(', and parameter ')}"
|
125
147
|
end
|
126
|
-
end
|
127
148
|
|
128
|
-
|
129
|
-
|
130
|
-
|
149
|
+
def generate_param_list(list, type)
|
150
|
+
output = []
|
151
|
+
list.each do |param, value|
|
152
|
+
if value.nil?
|
153
|
+
output << "#{param.to_s} #{type == :not ? 'un' : ''}defined"
|
154
|
+
else
|
155
|
+
a = type == :not ? '!' : '='
|
156
|
+
b = value.is_a?(Regexp) ? '~' : '>'
|
157
|
+
output << "#{param.to_s} #{a}#{b} #{value.inspect}"
|
158
|
+
end
|
159
|
+
end
|
160
|
+
output
|
161
|
+
end
|
162
|
+
|
163
|
+
def check_befores(catalogue, resource)
|
164
|
+
@befores.each do |ref|
|
165
|
+
unless precedes?(resource, catalogue.resource(ref))
|
166
|
+
@errors << BeforeRelationshipError.new(resource.to_ref, ref)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def check_requires(catalogue, resource)
|
172
|
+
@requires.each do |ref|
|
173
|
+
unless precedes?(catalogue.resource(ref), resource)
|
174
|
+
@errors << RequireRelationshipError.new(resource.to_ref, ref)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def check_notifies(catalogue, resource)
|
180
|
+
@notifies.each do |ref|
|
181
|
+
unless notifies?(resource, catalogue.resource(ref))
|
182
|
+
@errors << NotifyRelationshipError.new(resource.to_ref, ref)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def check_subscribes(catalogue, resource)
|
188
|
+
@subscribes.each do |ref|
|
189
|
+
unless notifies?(catalogue.resource(ref), resource)
|
190
|
+
@errors << SubscribeRelationshipError.new(resource.to_ref, ref)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def relationship_refs(array)
|
196
|
+
Array[array].flatten.map do |resource|
|
197
|
+
resource.respond_to?(:to_ref) ? resource.to_ref : resource
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def precedes?(first, second)
|
202
|
+
before_refs = relationship_refs(first[:before])
|
203
|
+
require_refs = relationship_refs(second[:require])
|
204
|
+
|
205
|
+
before_refs.include?(second.to_ref) || require_refs.include?(first.to_ref)
|
206
|
+
end
|
207
|
+
|
208
|
+
def notifies?(first, second)
|
209
|
+
notify_refs = relationship_refs(first[:notify])
|
210
|
+
subscribe_refs = relationship_refs(second[:subscribe])
|
211
|
+
|
212
|
+
notify_refs.include?(second.to_ref) || subscribe_refs.include?(first.to_ref)
|
213
|
+
end
|
214
|
+
|
215
|
+
# @param resource [Hash<Symbol, Object>] The resource in the catalog
|
216
|
+
# @param list [Array<String, Object>] The expected values of the resource
|
217
|
+
# @param type [:should, :not] Whether the given parameters should/not match
|
218
|
+
def check_params(resource, list, type)
|
219
|
+
list.each do |param, value|
|
220
|
+
param = param.to_sym
|
221
|
+
|
222
|
+
if value.nil? then
|
223
|
+
unless resource[param].nil?
|
224
|
+
@errors << "#{param} undefined"
|
225
|
+
end
|
226
|
+
else
|
227
|
+
m = ParameterMatcher.new(param, value, type)
|
228
|
+
unless m.matches?(resource)
|
229
|
+
@errors.concat m.errors
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
131
234
|
end
|
132
235
|
end
|
133
236
|
end
|