testbeat 0.5.0 → 0.5.1
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 +4 -4
- data/lib/{spec_helper.rb → rspec/spec_helper.rb} +0 -0
- data/lib/testbeat.rb +11 -4
- data/lib/vagrant/cookbook_decompiler.rb +73 -0
- data/lib/vagrant/noderunner.rb +290 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 02cef2aff2396b4839c9bd7af2bd8c99d1abf7df
|
4
|
+
data.tar.gz: c1de2a32d44fd2875ae8f472c5b6c5171ce61d0b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 44c1420a77283d2229cf547ed5d02cf3a28710e462b065ffbdbf2671caca746f4781bf65ddbd16b1441e3575c69579c7cdf3051c66139b617b566869ffa8155a
|
7
|
+
data.tar.gz: baa1fcd721c9febf81c6c989008b2462f63abcb6cae43d9b4ed473edfdc244979ff34f4108d45fe8726e06ed4998f7b42c0b9ad8c298664b002b0881ab2849e4
|
File without changes
|
data/lib/testbeat.rb
CHANGED
@@ -1,9 +1,16 @@
|
|
1
1
|
|
2
|
-
|
3
|
-
#require 'testbeat/vagrant'
|
4
|
-
require_relative './spec_helper.rb'
|
2
|
+
require 'rspec/spec_helper.rb' if defined? RSpec
|
5
3
|
|
6
|
-
#module
|
4
|
+
#module Testbeat
|
5
|
+
|
6
|
+
# https://route.github.io/2013/11/05/ruby-loading-and-requiring-files-constant-name-resolution.html
|
7
|
+
#autoload :Noderunner, 'vagrant/noderunner.rb'
|
8
|
+
|
9
|
+
#end
|
10
|
+
|
11
|
+
require 'vagrant/noderunner.rb'
|
12
|
+
|
13
|
+
#module Testbeat
|
7
14
|
#
|
8
15
|
# autoload :RSpec, './spec_helper.rb'
|
9
16
|
#
|
@@ -0,0 +1,73 @@
|
|
1
|
+
|
2
|
+
#!/usr/bin/ruby
|
3
|
+
|
4
|
+
require 'set'
|
5
|
+
|
6
|
+
$cookbooks_dir = config_path = File.join(Dir.pwd, "cookbooks")
|
7
|
+
|
8
|
+
def get_default_recipe(cookbook_name)
|
9
|
+
return File.open($cookbooks_dir + "/" + cookbook_name + "/recipes/default.rb","r")
|
10
|
+
end
|
11
|
+
|
12
|
+
def get_include_lines(file)
|
13
|
+
lines_with_include = []
|
14
|
+
file.each_line do |line|
|
15
|
+
if /include_recipe/.match(line)
|
16
|
+
lines_with_include.push(line)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
return lines_with_include
|
20
|
+
end
|
21
|
+
|
22
|
+
def get_cookbook_name(line)
|
23
|
+
name_match = /include_recipe.?"([^"]*)"/.match(line)
|
24
|
+
if name_match
|
25
|
+
just_name = name_match[1].split("::")[0]
|
26
|
+
return just_name
|
27
|
+
else
|
28
|
+
raise "Line #{line} does not contain an include_recipe statement"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def remove_recipe_part(name)
|
33
|
+
return name.split("::")[0]
|
34
|
+
end
|
35
|
+
|
36
|
+
def get_included_cookbooks(cookbook_name)
|
37
|
+
just_name = remove_recipe_part(cookbook_name)
|
38
|
+
recipe_default = get_default_recipe(just_name)
|
39
|
+
lines = get_include_lines(recipe_default)
|
40
|
+
names = []
|
41
|
+
lines.each do |line|
|
42
|
+
name = get_cookbook_name(line)
|
43
|
+
names.push(name) unless names.include? name
|
44
|
+
end
|
45
|
+
return names
|
46
|
+
end
|
47
|
+
|
48
|
+
module CookbookDecompiler
|
49
|
+
|
50
|
+
def CookbookDecompiler.resolve_dependencies(cookbook_names)
|
51
|
+
|
52
|
+
|
53
|
+
# First level cookbooks are obviously included, so let's make them the starting set.
|
54
|
+
cookbooks_to_be_returned = Set.new(cookbook_names)
|
55
|
+
|
56
|
+
loop do
|
57
|
+
# Next, find the second level cookbooks.
|
58
|
+
second_set = Set.new()
|
59
|
+
cookbooks_to_be_returned.each do |name|
|
60
|
+
included_cookbooks = get_included_cookbooks(name)
|
61
|
+
second_set = second_set.merge(included_cookbooks)
|
62
|
+
end
|
63
|
+
if second_set.subset? cookbooks_to_be_returned
|
64
|
+
return cookbooks_to_be_returned
|
65
|
+
else
|
66
|
+
cookbooks_to_be_returned.merge(second_set)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
# If all second level cookbooks are already in the set, we're done.
|
70
|
+
# otherwise we repeat, treating the second level cookbooks as first level.
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,290 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'json'
|
5
|
+
require 'pp'
|
6
|
+
require_relative './cookbook_decompiler.rb'
|
7
|
+
# dependencies for the lib running node tests, preferrably discovered here instead of in node loop
|
8
|
+
require 'rspec'
|
9
|
+
require 'set'
|
10
|
+
|
11
|
+
# Get first matching subgroup
|
12
|
+
# stripped of quotes and leading/trailing whitespace
|
13
|
+
def get_match(pattern, text)
|
14
|
+
matches = pattern.match(text)
|
15
|
+
the_match = matches[1]
|
16
|
+
the_match.delete("\"").strip()
|
17
|
+
end
|
18
|
+
|
19
|
+
# Prepared commands from run_tests are fed in here.
|
20
|
+
def run_integration_tests_bats(tests)
|
21
|
+
puts "Running bats tests for cookbooks."
|
22
|
+
|
23
|
+
tests.sort().each do |cmd|
|
24
|
+
#print "Running bats test #{cmd}"
|
25
|
+
system("cd #{ $vagrant_dir + $node }; vagrant ssh -c \"#{cmd}\"")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def run_integration_tests(recipes)
|
30
|
+
# Now its time for the bats tests.
|
31
|
+
integration_tests = []
|
32
|
+
repos_backup_testing = false
|
33
|
+
# Next we reduce recipes to cookbooks
|
34
|
+
if recipes.include?("repos-backup")
|
35
|
+
recipes.delete("repos-backup")
|
36
|
+
repos_backup_testing = true
|
37
|
+
end
|
38
|
+
|
39
|
+
integration_result_format = "--format RspecJunitFormatter"
|
40
|
+
node_path_to_cookbooks = ""
|
41
|
+
sync_data = File.read($vagrant_dir + $node + "/.vagrant/machines/default/virtualbox/synced_folders")
|
42
|
+
sync_info = JSON.parse(sync_data)
|
43
|
+
sync_info['virtualbox'].each do |key,value|
|
44
|
+
if /cookbooks/.match(value['hostpath'])
|
45
|
+
node_path_to_cookbooks = value['guestpath'].split("/")[0..-2].join("/")
|
46
|
+
break
|
47
|
+
end
|
48
|
+
end
|
49
|
+
recipes.each do |recipe|
|
50
|
+
# break off sub-recipe name from cookbook name
|
51
|
+
# repos-channel::haproxy -> repos-channel
|
52
|
+
puts "Recipe: #{recipe}"
|
53
|
+
cookbook_name = recipe.split("::")[0]
|
54
|
+
path_to_cookbook_integration_tests = "cookbooks/#{cookbook_name}/test/integration/default"
|
55
|
+
if Dir.exists?(path_to_cookbook_integration_tests)
|
56
|
+
puts "Found: #{path_to_cookbook_integration_tests}"
|
57
|
+
Dir.glob("#{path_to_cookbook_integration_tests}/*.rb").each do |path_to_file|
|
58
|
+
# /tmp/vagrant-chef-?/chef-solo-1/
|
59
|
+
# cd #{$vagrant_dir}#{node}; vagrant ssh -c
|
60
|
+
puts "node_path_to_cookbooks: #{node_path_to_cookbooks}. path_to_file: #{path_to_file}"
|
61
|
+
integration_tests.push("rspec #{integration_result_format} #{node_path_to_cookbooks}/#{path_to_file} > /vagrant/generated_integration_results_#{cookbook_name}.xml")
|
62
|
+
end
|
63
|
+
else
|
64
|
+
puts "Couldn't find #{path_to_cookbook_integration_tests}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
path_to_node_integration_tests = "#{$vagrant_dir}#{$node}/integration/default"
|
68
|
+
if Dir.exists?(path_to_node_integration_tests)
|
69
|
+
Dir.glob("#{path_to_node_integration_tests}/*.rb").each do |path_to_file|
|
70
|
+
just_filename = path_to_file.split("/")[-1]
|
71
|
+
integration_tests.push("rspec #{integration_result_format} /vagrant/integration/default/#{just_filename} > /vagrant/generated_integration_results_#{$node}.xml")
|
72
|
+
end
|
73
|
+
else
|
74
|
+
puts "No node-specific integration tests available for #{$node} in #{path_to_node_integration_tests}"
|
75
|
+
end
|
76
|
+
|
77
|
+
if integration_tests.empty?()
|
78
|
+
puts "No integration tests for node #{$node}"
|
79
|
+
else
|
80
|
+
run_integration_tests_bats(integration_tests)
|
81
|
+
end
|
82
|
+
if repos_backup_testing
|
83
|
+
system("cd #{ $vagrant_dir + $node }; vagrant ssh -c \"sudo chef-solo -j /tmp/vagrant-chef/dna.json -c /tmp/vagrant-chef/solo.rb -o 'recipe[cms-base],recipe[repos-backup]'\"")
|
84
|
+
end
|
85
|
+
|
86
|
+
#backup_test_cmd = "rspec -f j /tmp/vagrant-chef-?/chef-solo-1/cookbooks/repos-backup/test/integration/default/repos-backup_spec.rb > integration_repos-backup.txt"
|
87
|
+
#run_integration_tests_bats([backup_test_cmd])
|
88
|
+
end
|
89
|
+
|
90
|
+
def run_rspec(node, path, outf, verbose)
|
91
|
+
puts "Starting test: #{path}"
|
92
|
+
rspec_cmd = "NODE=#{node} rspec #{path} --format documentation --out #{outf}.txt --format html --out #{outf}.html --format RspecJunitFormatter --out #{outf}.xml --format progress"
|
93
|
+
IO.popen(rspec_cmd, :err=>[:child, :out], :external_encoding=>"UTF-8") do |io|
|
94
|
+
io.each_char do |c|
|
95
|
+
if verbose then
|
96
|
+
$stdout.print c
|
97
|
+
if c == '.' or c == 'F' or c == "\n" then $stdout.flush end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
return $?.success?
|
102
|
+
end
|
103
|
+
|
104
|
+
def run_tests(recipes, out: "#{$vagrant_dir}#{$node}/generated/", testglob: "test/acceptance/*.rb", verbose: true)
|
105
|
+
[recipes, out, testglob, verbose]
|
106
|
+
rspec_ok = true # if no tests => no failure
|
107
|
+
|
108
|
+
if Dir.exists?(out)
|
109
|
+
puts "Using existing output directory #{out}"
|
110
|
+
Dir.glob("#{out}acceptance*").each do |previous|
|
111
|
+
File.delete(previous);
|
112
|
+
end
|
113
|
+
else
|
114
|
+
puts "Creating output directory #{out}"
|
115
|
+
end
|
116
|
+
|
117
|
+
run_history = Set.new()
|
118
|
+
recipes.each do |recipe|
|
119
|
+
# break off sub-recipe name from cookbook name
|
120
|
+
# repos-channel::haproxy -> repos-channel
|
121
|
+
#puts "Recipe: " + recipe
|
122
|
+
cookbook_name = recipe.split("::")[0]
|
123
|
+
cookbook_specs = Dir.glob("cookbooks/#{cookbook_name}/#{testglob}")
|
124
|
+
if cookbook_specs.length == 0
|
125
|
+
puts "No generic acceptance tests available for #{recipe} in #{$chef_dir}cookbooks/#{cookbook_name}/test/acceptance"
|
126
|
+
end
|
127
|
+
cookbook_specs.each do |path_to_file|
|
128
|
+
if not run_history.include?(path_to_file)
|
129
|
+
run_history.add(path_to_file)
|
130
|
+
just_filename = path_to_file.split("/")[-1]
|
131
|
+
rspec_ok = run_rspec($node, path_to_file, "#{out}acceptance_#{cookbook_name}_#{just_filename}", verbose) && rspec_ok
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
path_to_node_acceptance_tests = "#{$vagrant_dir}#{$node}/acceptance"
|
137
|
+
if Dir.exists?(path_to_node_acceptance_tests)
|
138
|
+
rspec_ok = run_rspec($node, "#{path_to_node_acceptance_tests}/*.rb", "#{out}acceptance_node", verbose) && rspec_ok
|
139
|
+
else
|
140
|
+
puts "No node-specific acceptance tests available for #{$node} in #{path_to_node_acceptance_tests}"
|
141
|
+
end
|
142
|
+
|
143
|
+
# Print a summary in the end
|
144
|
+
concat = File.open("#{out}acceptance.txt", "w")
|
145
|
+
Dir.glob("#{out}acceptance_*.txt").each do |generated_docs|
|
146
|
+
puts generated_docs
|
147
|
+
concat.write("#" + generated_docs)
|
148
|
+
File.readlines(generated_docs).each do |line|
|
149
|
+
concat.write(line)
|
150
|
+
results = /^\d+ examples, (\d+) failure.?/.match(line)
|
151
|
+
if results
|
152
|
+
puts line
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
concat.close unless concat.nil?
|
157
|
+
|
158
|
+
return rspec_ok
|
159
|
+
end
|
160
|
+
|
161
|
+
# guestint: run on-guest integration tests
|
162
|
+
def main(node: "labs01", provider: "virtualbox", retest: false, guestint: true, verbose: true)
|
163
|
+
[node, provider, retest, guestint, verbose]
|
164
|
+
$node = node
|
165
|
+
options = {}
|
166
|
+
|
167
|
+
$chef_dir = ""
|
168
|
+
$vagrant_dir = $chef_dir + "nodes/"
|
169
|
+
$bats_test_tmp = $vagrant_dir + "bats_tmp/"
|
170
|
+
|
171
|
+
$vagrant_file = $vagrant_dir + $node + "/Vagrantfile"
|
172
|
+
#$vagrant_chef_dir = %x[ cd #{$vagrant_dir}#{node}; vagrant ssh -c "find /tmp/vagrant-chef/ -maxdepth 2 -type d -name cookbooks ]
|
173
|
+
#"/tmp/vagrant-chef/chef-solo-1/"
|
174
|
+
|
175
|
+
puts "### node: #{$node} (#{$vagrant_dir + $node}) ###"
|
176
|
+
recipes = []
|
177
|
+
|
178
|
+
|
179
|
+
if not (Dir.exists?($vagrant_dir + $node) and File.exists?($vagrant_file))
|
180
|
+
$stderr.puts "No such Vagrant node #{ $node }"
|
181
|
+
exit 1
|
182
|
+
end
|
183
|
+
|
184
|
+
# ----------------------------------------------------------------------------------
|
185
|
+
# Start Vagrant or run provision on an already running node
|
186
|
+
|
187
|
+
cwd_to_node = "cd #{ $vagrant_dir + $node}; "
|
188
|
+
|
189
|
+
v_status = %x[ cd #{ $vagrant_dir + $node}; vagrant status ]
|
190
|
+
runlist_file = "/tmp/#{$node}_testbeat.runlist";
|
191
|
+
if /poweroff/.match(v_status) or /not created/.match(v_status)
|
192
|
+
puts "Vagrant node not running, start and provision..."
|
193
|
+
if File.exists?(runlist_file)
|
194
|
+
File.delete(runlist_file)
|
195
|
+
end
|
196
|
+
vagrant_cmd = cwd_to_node + "vagrant up --provider=#{provider}"
|
197
|
+
elsif /running/.match(v_status)
|
198
|
+
# Add "if runlist file older than 1 h, assume force_long"
|
199
|
+
if retest and File.exists?(runlist_file)
|
200
|
+
old_run = File.read(runlist_file)
|
201
|
+
#run_match = /Run List expands to \[(.*?)\]/.match(old_run)
|
202
|
+
recipes = old_run.split(", ")
|
203
|
+
print "Recipes (rerun based on #{runlist_file}): "
|
204
|
+
puts recipes
|
205
|
+
all_cookbooks = CookbookDecompiler.resolve_dependencies(recipes).to_a
|
206
|
+
puts "All cookbooks included: " + all_cookbooks.join(", ")
|
207
|
+
# code duplicated from uncached runlist below
|
208
|
+
rspec_ok = true
|
209
|
+
if guestint
|
210
|
+
rspec_ok = rspec_ok && run_integration_tests(all_cookbooks)
|
211
|
+
end
|
212
|
+
rspec_ok = rspec_ok && run_tests(all_cookbooks)
|
213
|
+
if not rspec_ok
|
214
|
+
puts "There were test failures!"
|
215
|
+
exit 1
|
216
|
+
end
|
217
|
+
puts "All tests for cached runlist passed"
|
218
|
+
exit 0
|
219
|
+
else
|
220
|
+
puts "Vagrant node running, provision..."
|
221
|
+
vagrant_cmd = cwd_to_node + "vagrant provision"
|
222
|
+
end
|
223
|
+
else
|
224
|
+
$stderr.puts "Unknown Vagrant state: #{v_status}"
|
225
|
+
end
|
226
|
+
|
227
|
+
# ----------------------------------------------------------------------------------
|
228
|
+
# Build an array consisting of custom tests to be compared with Vagrant provision
|
229
|
+
# output
|
230
|
+
|
231
|
+
# First we look up tests for our custom Vagrant output checker
|
232
|
+
test_collection = []
|
233
|
+
|
234
|
+
if options[:tests]
|
235
|
+
tests = options[:tests].split(",")
|
236
|
+
tests.each do |opt|
|
237
|
+
test_file_path = opt
|
238
|
+
if File.exists?(test_file_path)
|
239
|
+
contents = File.read(test_file_path)
|
240
|
+
obj = JSON.parse(contents)
|
241
|
+
obj["tests"].each do |test|
|
242
|
+
test_collection.push(test)
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
#vagrant_run_output = %x[ export LANG=en_US.UTF-8; #{vagrant_cmd} ]
|
249
|
+
vagrant_run_output = ''
|
250
|
+
IO.popen(vagrant_cmd, :err=>[:child, :out], :external_encoding=>"UTF-8") do |io|
|
251
|
+
io.each do |line|
|
252
|
+
if verbose then puts line end
|
253
|
+
vagrant_run_output << line + "\n"
|
254
|
+
end
|
255
|
+
end
|
256
|
+
result = $?.success?
|
257
|
+
|
258
|
+
if not result
|
259
|
+
$stderr.puts "Vagrant run failed! See output below"
|
260
|
+
$stderr.puts vagrant_run_output
|
261
|
+
exit 1
|
262
|
+
else
|
263
|
+
puts "Vagrant provision completed."
|
264
|
+
# Run List expands to [repos-channel::haproxy, cms-base::folderstructure, repos-apache2, repos-subversion, repos-rweb, repos-trac, repos-liveserver, repos-indexing, repos-snapshot, repos-vagrant-labs]
|
265
|
+
run_match = /Run List expands to \[(.*?)\]/.match(vagrant_run_output)
|
266
|
+
if run_match
|
267
|
+
dump_file = File.new("/tmp/#{$node}_testbeat.runlist","w+",0755)
|
268
|
+
dump_file.write(run_match[1]) # should be run_match[1] but role-leanserver edit above...
|
269
|
+
dump_file.close()
|
270
|
+
|
271
|
+
recipes = run_match[1].split(", ")
|
272
|
+
puts "Run list extracted from Vagrant: " + recipes.join(", ")
|
273
|
+
all_cookbooks = CookbookDecompiler.resolve_dependencies(recipes).to_a
|
274
|
+
puts "All cookbooks included: " + all_cookbooks.join(", ")
|
275
|
+
puts "test_collection (presumably not used anymore): " + test_collection.join(", ");
|
276
|
+
# the run code has been duplicated for cached runlist above
|
277
|
+
rspec_ok = true
|
278
|
+
if guestint
|
279
|
+
rspec_ok = rspec_ok && run_integration_tests(all_cookbooks)
|
280
|
+
end
|
281
|
+
rspec_ok = rspec_ok && run_tests(all_cookbooks)
|
282
|
+
if not rspec_ok
|
283
|
+
exit 1
|
284
|
+
end
|
285
|
+
else
|
286
|
+
puts "Unable to find text 'Run List expands to' in Vagrant output :("
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: testbeat
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Staffan Olsson
|
@@ -60,7 +60,9 @@ extensions: []
|
|
60
60
|
extra_rdoc_files: []
|
61
61
|
files:
|
62
62
|
- lib/testbeat.rb
|
63
|
-
- lib/spec_helper.rb
|
63
|
+
- lib/rspec/spec_helper.rb
|
64
|
+
- lib/vagrant/noderunner.rb
|
65
|
+
- lib/vagrant/cookbook_decompiler.rb
|
64
66
|
homepage: https://github.com/Reposoft/testbeat
|
65
67
|
licenses:
|
66
68
|
- MIT
|