liri 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +4 -2
- data/.simplecov +8 -0
- data/Gemfile +4 -1
- data/Gemfile.lock +27 -15
- data/bash_script_examples.sh +1 -0
- data/dummy-app/.gitignore +12 -0
- data/dummy-app/.rspec +3 -0
- data/dummy-app/.ruby-gemset +1 -0
- data/dummy-app/.ruby-version +1 -0
- data/dummy-app/CODE_OF_CONDUCT.md +84 -0
- data/dummy-app/Gemfile +10 -0
- data/dummy-app/Gemfile.lock +35 -0
- data/dummy-app/LICENSE.txt +21 -0
- data/dummy-app/README.md +43 -0
- data/dummy-app/Rakefile +8 -0
- data/dummy-app/bin/console +15 -0
- data/dummy-app/bin/setup +8 -0
- data/dummy-app/dummy-app.gemspec +39 -0
- data/dummy-app/lib/dummy/app/version.rb +7 -0
- data/dummy-app/lib/dummy/app.rb +10 -0
- data/dummy-app/spec/dummy/app_spec.rb +11 -0
- data/dummy-app/spec/spec_helper.rb +15 -0
- data/exe/liri +9 -4
- data/lib/agent/agent.rb +90 -50
- data/lib/agent/runner.rb +1 -3
- data/lib/all_libraries.rb +4 -2
- data/lib/common/benchmarking.rb +5 -10
- data/lib/common/log.rb +12 -6
- data/lib/common/manager_data.rb +30 -0
- data/lib/common/progressbar.rb +29 -0
- data/lib/common/setup.rb +103 -0
- data/lib/common/source_code.rb +22 -12
- data/lib/common/tests_result.rb +115 -0
- data/lib/common/unit_test/rspec.rb +15 -23
- data/lib/liri.rb +22 -35
- data/lib/manager/manager.rb +163 -98
- data/lib/task.rb +2 -2
- data/liri.gemspec +8 -0
- data/spec_credentials.yml.example +4 -0
- data/template/liri-config.yml +4 -2
- metadata +54 -4
- data/lib/manager/setup.rb +0 -70
- data/lib/manager/test_result.rb +0 -38
@@ -0,0 +1,115 @@
|
|
1
|
+
# = tests_result.rb
|
2
|
+
#
|
3
|
+
# @author Rodrigo Fernández
|
4
|
+
#
|
5
|
+
# == Clase TestsResult
|
6
|
+
|
7
|
+
module Liri
|
8
|
+
module Common
|
9
|
+
# Esta clase se encarga de guardar y procesar el archivo de resultados
|
10
|
+
class TestsResult
|
11
|
+
def initialize(folder_path)
|
12
|
+
@folder_path = folder_path
|
13
|
+
@example_quantity = 0
|
14
|
+
@failure_quantity = 0
|
15
|
+
@passed_quantity = 0
|
16
|
+
@failures = ''
|
17
|
+
end
|
18
|
+
|
19
|
+
def save(file_name, raw_tests_result)
|
20
|
+
file_path = File.join(@folder_path, '/', file_name)
|
21
|
+
File.write(file_path, raw_tests_result)
|
22
|
+
file_path
|
23
|
+
end
|
24
|
+
|
25
|
+
def build_file_name(agent_ip_address, tests_batch_number)
|
26
|
+
"batch_#{tests_batch_number}_agent_#{agent_ip_address}_tests_results"
|
27
|
+
end
|
28
|
+
|
29
|
+
# Procesa el resultado crudo de las pruebas unitarias y lo devuelve en formato hash manejable
|
30
|
+
# Ejemplo del hash retornado:
|
31
|
+
# {example_quantity: 2, failure_quantity: 1}
|
32
|
+
def process(tests_result_file_name)
|
33
|
+
file_path = File.join(@folder_path, '/', tests_result_file_name)
|
34
|
+
result_hash = process_tests_result_file(file_path)
|
35
|
+
update_partial_result(result_hash)
|
36
|
+
#print_partial_result(result_hash)
|
37
|
+
result_hash
|
38
|
+
end
|
39
|
+
|
40
|
+
def print_summary
|
41
|
+
puts "\n#{@example_quantity} examples, #{@failure_quantity} failures\n"
|
42
|
+
end
|
43
|
+
|
44
|
+
def print_failures
|
45
|
+
puts "\nFailures: " if !@failures.empty?
|
46
|
+
puts @failures
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
# Recibe el resultado crudo de las pruebas unitarias
|
52
|
+
# Procesa el archivo con los resultados crudos y lo devuelve en formato hash manejable
|
53
|
+
# Ejemplo del hash retornado:
|
54
|
+
# {result: '.F', failures: '', example_quantity: 2, failure_quantity: 1, failed_examples: ''}
|
55
|
+
def process_tests_result_file(file_path)
|
56
|
+
result_hash = {failures: '', example_quantity: 0, failure_quantity: 0, passed_quantity: 0, failed_examples: ''}
|
57
|
+
flag = ''
|
58
|
+
File.foreach(file_path) do |line|
|
59
|
+
if flag == '' && line.strip.start_with?('Randomized')
|
60
|
+
flag = 'Randomized'
|
61
|
+
next
|
62
|
+
end
|
63
|
+
|
64
|
+
if ['Randomized', ''].include?(flag) && line.strip.start_with?('Failures')
|
65
|
+
flag = 'Failures'
|
66
|
+
next
|
67
|
+
end
|
68
|
+
|
69
|
+
if ['Randomized', 'Failures', ''].include?(flag) && line.strip.start_with?('Finished')
|
70
|
+
flag = 'Finished'
|
71
|
+
next
|
72
|
+
end
|
73
|
+
|
74
|
+
if ['Finished', ''].include?(flag) && line.strip.start_with?('Failed')
|
75
|
+
flag = 'Failed'
|
76
|
+
next
|
77
|
+
end
|
78
|
+
|
79
|
+
case flag
|
80
|
+
when 'Failures'
|
81
|
+
result_hash[:failures] << line
|
82
|
+
when 'Finished'
|
83
|
+
values = line.to_s.match(/([\d]+) example.?, ([\d]+) failure.?/)
|
84
|
+
result_hash[:example_quantity] = values[1].to_i
|
85
|
+
result_hash[:failure_quantity] = values[2].to_i
|
86
|
+
result_hash[:passed_quantity] = result_hash[:example_quantity] - result_hash[:failure_quantity]
|
87
|
+
flag = ''
|
88
|
+
when 'Failed'
|
89
|
+
result_hash[:failed_examples] << line
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
result_hash
|
94
|
+
end
|
95
|
+
|
96
|
+
def update_partial_result(hash_result)
|
97
|
+
@example_quantity += hash_result[:example_quantity]
|
98
|
+
@failure_quantity += hash_result[:failure_quantity]
|
99
|
+
@passed_quantity += hash_result[:passed_quantity]
|
100
|
+
@failures << hash_result[:failures]
|
101
|
+
end
|
102
|
+
|
103
|
+
def print_partial_result(result_hash)
|
104
|
+
result_hash[:passed_quantity].times do
|
105
|
+
print '.'
|
106
|
+
end
|
107
|
+
|
108
|
+
result_hash[:failure_quantity].times do
|
109
|
+
print 'F'
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -16,7 +16,10 @@ module Liri
|
|
16
16
|
tests_hash = {}
|
17
17
|
test_files.each do |test_file|
|
18
18
|
File.open(test_file) do |file|
|
19
|
+
@inside_comment = false
|
19
20
|
file.each_with_index do |line, index|
|
21
|
+
next if line_inside_comment_block(line)
|
22
|
+
|
20
23
|
if line.strip.start_with?('it')
|
21
24
|
absolute_file_path = file.to_path
|
22
25
|
relative_file_path = absolute_file_path.sub(@source_code_folder_path + '/', '')
|
@@ -50,40 +53,29 @@ module Liri
|
|
50
53
|
raw_tests_result = %x|bash -lc 'rvm use #{Liri.current_folder_ruby_and_gemset}; rspec #{tests.join(' ')} --format progress'|
|
51
54
|
end
|
52
55
|
|
53
|
-
|
54
|
-
hash_tests_result
|
56
|
+
return raw_tests_result
|
55
57
|
end
|
56
58
|
end
|
57
59
|
|
58
60
|
private
|
61
|
+
|
59
62
|
def test_files
|
60
63
|
Dir[@tests_folder_path + "/**/*spec.rb"]
|
61
64
|
end
|
62
65
|
|
63
|
-
#
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
flag = ''
|
70
|
-
raw_test_results.each_line do |line|
|
71
|
-
if line.strip.start_with?('Finished')
|
72
|
-
flag = 'Finished'
|
73
|
-
next
|
74
|
-
end
|
66
|
+
# Revisa si la línea se encuentra dentro de un bloque comentado
|
67
|
+
def line_inside_comment_block(line)
|
68
|
+
if line.strip.start_with?('=begin')
|
69
|
+
@inside_comment = true
|
70
|
+
return true
|
71
|
+
end
|
75
72
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
values = line.to_s.match(/([\d]+) example.?, ([\d]+) failure.?/)
|
80
|
-
result_hash[:example_quantity] = values[1].to_i
|
81
|
-
result_hash[:failure_quantity] = values[2].to_i
|
82
|
-
flag = ''
|
83
|
-
end
|
73
|
+
if line.strip.start_with?('=end')
|
74
|
+
@inside_comment = false
|
75
|
+
return false
|
84
76
|
end
|
85
77
|
|
86
|
-
|
78
|
+
return true if @inside_comment
|
87
79
|
end
|
88
80
|
end
|
89
81
|
end
|
data/lib/liri.rb
CHANGED
@@ -3,20 +3,16 @@
|
|
3
3
|
# Este modulo contiene datos del programa que son reutilizados en otras partes de la aplicacion
|
4
4
|
module Liri
|
5
5
|
NAME = 'liri' # El gemspec requiere que el nombre este en minusculas
|
6
|
-
VERSION = '0.
|
7
|
-
SETUP_FOLDER_NAME = 'liri'
|
8
|
-
SETUP_FOLDER_PATH = File.join(Dir.pwd, '/', SETUP_FOLDER_NAME)
|
9
|
-
LOGS_FOLDER_NAME = 'logs'
|
10
|
-
MANAGER_LOGS_FOLDER_PATH = File.join(SETUP_FOLDER_PATH, '/', LOGS_FOLDER_NAME)
|
11
|
-
AGENT_LOGS_FOLDER_PATH = MANAGER_LOGS_FOLDER_PATH
|
12
|
-
AGENT_FOLDER_NAME = 'agent'
|
13
|
-
AGENT_FOLDER_PATH = File.join(SETUP_FOLDER_PATH, '/', AGENT_FOLDER_NAME)
|
14
|
-
MANAGER_FOLDER_NAME = 'manager'
|
15
|
-
MANAGER_FOLDER_PATH = File.join(SETUP_FOLDER_PATH, '/', MANAGER_FOLDER_NAME)
|
6
|
+
VERSION = '0.2.0'
|
16
7
|
|
17
8
|
class << self
|
9
|
+
def set_setup(destination_folder_path)
|
10
|
+
load_setup_manager(destination_folder_path)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Carga las configuraciones en memoria desde un archivo de configuracion
|
18
14
|
def setup
|
19
|
-
@setup
|
15
|
+
@setup
|
20
16
|
end
|
21
17
|
|
22
18
|
def logger
|
@@ -36,30 +32,16 @@ module Liri
|
|
36
32
|
end
|
37
33
|
end
|
38
34
|
|
39
|
-
def
|
40
|
-
Dir.mkdir(SETUP_FOLDER_PATH) unless Dir.exist?(SETUP_FOLDER_PATH)
|
41
|
-
|
42
|
-
case program
|
43
|
-
when 'manager'
|
44
|
-
Dir.mkdir(MANAGER_LOGS_FOLDER_PATH) unless Dir.exist?(MANAGER_LOGS_FOLDER_PATH)
|
45
|
-
Dir.mkdir(MANAGER_FOLDER_PATH) unless Dir.exist?(MANAGER_FOLDER_PATH)
|
46
|
-
when 'agent'
|
47
|
-
Dir.mkdir(AGENT_FOLDER_PATH) unless Dir.exist?(AGENT_FOLDER_PATH)
|
48
|
-
Dir.mkdir(AGENT_LOGS_FOLDER_PATH) unless Dir.exist?(AGENT_LOGS_FOLDER_PATH)
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
def clean_folder(folder_path)
|
35
|
+
def clean_folder_content(folder_path)
|
53
36
|
FileUtils.rm_rf(Dir.glob(folder_path + '/*')) if Dir.exist?(folder_path)
|
54
37
|
end
|
55
38
|
|
56
39
|
def reload_setup
|
57
|
-
@setup =
|
40
|
+
@setup = (@setup_manager ? @setup_manager.load : nil)
|
58
41
|
end
|
59
42
|
|
60
43
|
def delete_setup
|
61
|
-
|
62
|
-
liri_setup.delete
|
44
|
+
@setup_manager ? @setup_manager.delete_setup_folder : false
|
63
45
|
end
|
64
46
|
|
65
47
|
def init_exit(stop, threads, program)
|
@@ -75,7 +57,7 @@ module Liri
|
|
75
57
|
end
|
76
58
|
|
77
59
|
def kill(threads)
|
78
|
-
threads.each{|thread| Thread.kill(thread)}
|
60
|
+
threads.each{ |thread| Thread.kill(thread) }
|
79
61
|
end
|
80
62
|
|
81
63
|
def current_host_ip_address
|
@@ -99,22 +81,27 @@ module Liri
|
|
99
81
|
setup.ports.tcp
|
100
82
|
end
|
101
83
|
|
84
|
+
def print_failures
|
85
|
+
setup.print_failures
|
86
|
+
end
|
87
|
+
|
102
88
|
def current_folder_ruby_and_gemset
|
103
89
|
"#{File.read('.ruby-version').strip}@#{File.read('.ruby-gemset').strip}"
|
104
90
|
end
|
105
91
|
|
106
92
|
private
|
107
93
|
|
108
|
-
#
|
109
|
-
def
|
110
|
-
|
111
|
-
|
112
|
-
|
94
|
+
# Inicializa el objeto que gestiona las configuraciones
|
95
|
+
def load_setup_manager(destination_folder_path)
|
96
|
+
@setup_manager = Liri::Common::Setup.new(destination_folder_path)
|
97
|
+
@setup_manager.init
|
98
|
+
@setup = @setup_manager.load
|
99
|
+
@setup_manager
|
113
100
|
end
|
114
101
|
|
115
102
|
# Inicializa y configura la librería encargada de loguear
|
116
103
|
def load_logger(folder_path = nil, file_name = nil)
|
117
|
-
log = Liri::Common::Log.new('daily', folder_path: folder_path, file_name: file_name, stdout:
|
104
|
+
log = Liri::Common::Log.new('daily', folder_path: folder_path, file_name: file_name, stdout: setup.log.stdout.show)
|
118
105
|
log
|
119
106
|
end
|
120
107
|
end
|
data/lib/manager/manager.rb
CHANGED
@@ -1,7 +1,11 @@
|
|
1
1
|
=begin
|
2
2
|
Este modulo es el punto de entrada del programa principal
|
3
3
|
=end
|
4
|
+
|
5
|
+
# TODO Para trabajar con hilos y concurrencia recomiendan usar parallel, workers, concurrent-ruby. Fuente: https://www.rubyguides.com/2015/07/ruby-threads/
|
6
|
+
|
4
7
|
require 'all_libraries'
|
8
|
+
require 'terminal-table'
|
5
9
|
|
6
10
|
module Liri
|
7
11
|
class Manager
|
@@ -11,29 +15,27 @@ module Liri
|
|
11
15
|
class << self
|
12
16
|
# Inicia la ejecución del Manager
|
13
17
|
# @param stop [Boolean] el valor true es para que no se ejecute infinitamente el método en el test unitario.
|
14
|
-
def run(stop = false)
|
18
|
+
def run(source_code_folder_path, stop = false)
|
15
19
|
return unless valid_project
|
16
|
-
Liri.create_folders('manager')
|
17
|
-
|
18
|
-
Liri.set_logger(Liri::MANAGER_LOGS_FOLDER_PATH, 'liri-manager.log')
|
19
|
-
Liri.logger.info("Proceso Manager iniciado")
|
20
|
-
puts "Presione Ctrl + c para terminar el proceso Manager manualmente\n\n"
|
21
20
|
|
22
|
-
|
21
|
+
setup_manager = Liri.set_setup(source_code_folder_path)
|
22
|
+
manager_folder_path = setup_manager.manager_folder_path
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
|
24
|
+
Liri.set_logger(setup_manager.logs_folder_path, 'liri-manager.log')
|
25
|
+
Liri.logger.info("Proceso Manager iniciado")
|
26
|
+
Liri.logger.info("Presione Ctrl + c para terminar el proceso Manager manualmente\n", true)
|
27
27
|
|
28
|
+
user, password = get_credentials(setup_manager.setup_folder_path)
|
29
|
+
source_code = compress_source_code(source_code_folder_path, manager_folder_path)
|
30
|
+
manager_data = get_manager_data(user, password, manager_folder_path, source_code)
|
28
31
|
all_tests = get_all_tests(source_code)
|
32
|
+
tests_result = Common::TestsResult.new(manager_folder_path)
|
29
33
|
|
30
|
-
|
31
|
-
|
32
|
-
manager = Manager.new(Liri.udp_port, Liri.tcp_port, all_tests, test_result)
|
34
|
+
manager = Manager.new(Liri.udp_port, Liri.tcp_port, all_tests, tests_result, manager_folder_path)
|
33
35
|
|
34
36
|
threads = []
|
35
37
|
threads << manager.start_client_socket_to_search_agents(manager_data) # Enviar peticiones broadcast a toda la red para encontrar Agents
|
36
|
-
manager.start_server_socket_to_process_tests(threads[0]) # Esperar y enviar los test unitarios a los Agents
|
38
|
+
manager.start_server_socket_to_process_tests(threads[0]) unless stop # Esperar y enviar los test unitarios a los Agents
|
37
39
|
|
38
40
|
Liri.init_exit(stop, threads, 'Manager')
|
39
41
|
Liri.logger.info("Proceso Manager terminado")
|
@@ -58,44 +60,44 @@ module Liri
|
|
58
60
|
end
|
59
61
|
end
|
60
62
|
|
61
|
-
def
|
62
|
-
|
63
|
-
end
|
64
|
-
|
65
|
-
def decompressed_file_folder_path
|
66
|
-
compressed_file_folder_path
|
67
|
-
end
|
68
|
-
|
69
|
-
def get_credentials
|
70
|
-
credential = Liri::Manager::Credential.new(Liri::SETUP_FOLDER_PATH)
|
63
|
+
def get_credentials(setup_folder_path)
|
64
|
+
credential = Liri::Manager::Credential.new(setup_folder_path)
|
71
65
|
credential.get
|
72
66
|
end
|
73
67
|
|
74
|
-
def compress_source_code
|
75
|
-
source_code =
|
76
|
-
|
68
|
+
def compress_source_code(source_code_folder_path, manager_folder_path)
|
69
|
+
source_code = Common::SourceCode.new(source_code_folder_path, manager_folder_path, Liri.compression_class, Liri.unit_test_class)
|
70
|
+
|
71
|
+
Common::Progressbar.start(total: nil, length: 100, format: 'Comprimiendo Código Fuente |%B| %a') do
|
77
72
|
source_code.compress_folder
|
78
73
|
end
|
79
|
-
puts
|
74
|
+
puts "\n\n"
|
75
|
+
|
80
76
|
source_code
|
81
77
|
end
|
82
78
|
|
83
|
-
def
|
84
|
-
|
85
|
-
|
79
|
+
def get_manager_data(user, password, manager_folder_path, source_code)
|
80
|
+
Common::ManagerData.new(
|
81
|
+
folder_path: manager_folder_path,
|
82
|
+
compressed_file_path: source_code.compressed_file_path,
|
83
|
+
user: user,
|
84
|
+
password: password
|
85
|
+
)
|
86
86
|
end
|
87
87
|
|
88
88
|
def get_all_tests(source_code)
|
89
89
|
all_tests = {}
|
90
|
-
|
90
|
+
|
91
|
+
Common::Progressbar.start(total: nil, length: 100, format: 'Extrayendo Pruebas Unitarias |%B| %a') do
|
91
92
|
all_tests = source_code.all_tests
|
92
93
|
end
|
93
|
-
puts
|
94
|
+
puts "\n\n"
|
95
|
+
|
94
96
|
all_tests
|
95
97
|
end
|
96
98
|
end
|
97
99
|
|
98
|
-
def initialize(udp_port, tcp_port, all_tests,
|
100
|
+
def initialize(udp_port, tcp_port, all_tests, tests_result, manager_folder_path)
|
99
101
|
@udp_port = udp_port
|
100
102
|
@udp_socket = UDPSocket.new
|
101
103
|
@tcp_port = tcp_port
|
@@ -110,22 +112,29 @@ module Liri
|
|
110
112
|
@agents_search_processing_enabled = true
|
111
113
|
@test_processing_enabled = true
|
112
114
|
|
113
|
-
@
|
115
|
+
@tests_batch_number = 0
|
116
|
+
@tests_batches = {}
|
117
|
+
|
118
|
+
@tests_result = tests_result
|
114
119
|
@semaphore = Mutex.new
|
120
|
+
|
121
|
+
@manager_folder_path = manager_folder_path
|
122
|
+
|
123
|
+
@progressbar = ProgressBar.create(starting_at: 0, total: @all_tests_count, length: 100, format: 'Progress %c/%C |%b=%i| %p%% | %a')
|
115
124
|
end
|
116
125
|
|
117
126
|
# Inicia un cliente udp que hace un broadcast en toda la red para iniciar una conexión con los Agent que estén escuchando
|
118
|
-
def start_client_socket_to_search_agents(
|
127
|
+
def start_client_socket_to_search_agents(manager_data)
|
119
128
|
# El cliente udp se ejecuta en bucle dentro de un hilo, esto permite realizar otras tareas mientras este hilo sigue sondeando
|
120
129
|
# la red para obtener mas Agents. Una vez que los tests terminan de ejecutarse, este hilo será finalizado.
|
121
130
|
Thread.new do
|
122
|
-
|
131
|
+
Liri.logger.info("Buscando Agentes... Espere")
|
123
132
|
Liri.logger.info("Se emite un broadcast cada #{UDP_REQUEST_DELAY} segundos en el puerto UDP: #{@udp_port}
|
124
133
|
(Se mantiene escaneando la red para encontrar Agents)
|
125
134
|
")
|
126
|
-
while
|
135
|
+
while agents_search_processing_enabled
|
127
136
|
@udp_socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_BROADCAST, true)
|
128
|
-
@udp_socket.send(
|
137
|
+
@udp_socket.send(manager_data.to_h.to_json, 0, '<broadcast>', @udp_port)
|
129
138
|
sleep(UDP_REQUEST_DELAY) # Se pausa un momento antes de efectuar nuevamente la petición broadcast
|
130
139
|
end
|
131
140
|
end
|
@@ -146,12 +155,18 @@ module Liri
|
|
146
155
|
")
|
147
156
|
# El siguiente bucle permite que varios clientes es decir Agents se conecten
|
148
157
|
# De: http://www.w3big.com/es/ruby/ruby-socket-programming.html
|
149
|
-
while
|
158
|
+
while test_processing_enabled
|
150
159
|
Thread.start(tcp_socket.accept) do |client|
|
151
160
|
agent_ip_address = client.remote_address.ip_address
|
152
161
|
response = client.recvfrom(1000).first
|
153
162
|
|
154
|
-
|
163
|
+
Liri.logger.info("\nConexión iniciada con el Agente: #{agent_ip_address}")
|
164
|
+
Liri.logger.info("Respuesta al broadcast recibida del Agent: #{agent_ip_address} en el puerto TCP: #{@tcp_port}: #{response}")
|
165
|
+
|
166
|
+
# Se le indica al agente que proceda
|
167
|
+
client.puts({ msg: 'Recibido', exist_tests: all_tests.any? }.to_json)
|
168
|
+
|
169
|
+
if all_tests.empty?
|
155
170
|
# No importa lo que le haga, el broadcast udp no se muere al instante y el agente sigue respondiendo
|
156
171
|
# Las siguientes dos lineas son para que se deje de hacer el broadcast pero aun asi se llegan a hacer
|
157
172
|
# 3 a 4 broadcast antes de que se finalize el proceso, al parecer el broadcast va a tener que quedar asi
|
@@ -159,49 +174,42 @@ module Liri
|
|
159
174
|
# tests enviados sin resultados, tests finalizados, si se recibe respuesta al broadcast se trata de enviar primero test pendientes
|
160
175
|
# luego test enviados sin resultados o sino ignorar
|
161
176
|
Thread.kill(search_agents_thread)
|
162
|
-
|
163
|
-
Liri.logger.info("Se termina cualquier proceso pendiente con el Agent #{agent_ip_address}")
|
164
|
-
Liri.logger.info(response)
|
177
|
+
agents_search_processing_enabled = false
|
178
|
+
Liri.logger.info("Se termina cualquier proceso pendiente con el Agent #{agent_ip_address} en el puerto TCP: #{@tcp_port}: #{response}")
|
165
179
|
client.close
|
166
180
|
Thread.exit
|
167
181
|
end
|
168
182
|
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
183
|
+
while all_tests.any?
|
184
|
+
time_in_seconds = Liri::Common::Benchmarking.start(start_msg: "Proceso de Ejecución de pruebas. Agent: #{agent_ip_address}. Espere... ", end_msg: "Proceso de Ejecución de pruebas. Agent: #{agent_ip_address}. Duración: ", stdout: false) do
|
185
|
+
tests_batch = tests_batch(agent_ip_address)
|
186
|
+
break unless tests_batch
|
187
|
+
|
188
|
+
begin
|
189
|
+
Liri.logger.debug("Conjunto de pruebas enviadas al Agent #{agent_ip_address}: #{tests_batch}")
|
190
|
+
|
191
|
+
client.puts(tests_batch.to_json) # Se envia el lote de tests
|
192
|
+
response = client.recvfrom(1000).first # Se recibe la respuesta. Cuando mas alto es el parámetro de recvfrom, mas datos se reciben osino se truncan.
|
193
|
+
rescue Errno::EPIPE => e
|
194
|
+
# Esto al parecer se da cuando el Agent ya cerró las conexiones y el Manager intenta contactar
|
195
|
+
Liri.logger.error("Exception(#{e}) El Agent #{agent_ip_address} ya terminó la conexión")
|
196
|
+
# Si el Agente ya no responde es mejor romper el bucle para que no quede colgado
|
197
|
+
break
|
198
|
+
end
|
185
199
|
end
|
186
|
-
|
187
|
-
#
|
200
|
+
|
201
|
+
# Se captura por si acaso los errores de parseo JSON
|
188
202
|
begin
|
189
|
-
tests_result = response
|
190
|
-
Liri.logger.debug("
|
191
|
-
|
192
|
-
Liri.logger.debug(tests_result)
|
193
|
-
json_tests_result = JSON.parse(tests_result)
|
194
|
-
Liri.logger.debug("JSON:")
|
195
|
-
Liri.logger.debug(json_tests_result)
|
196
|
-
process_tests_result(tests, json_tests_result)
|
203
|
+
tests_result = JSON.parse(response)
|
204
|
+
Liri.logger.debug("Respuesta del Agent #{agent_ip_address}: #{tests_result}")
|
205
|
+
process_tests_result(agent_ip_address, tests_result, time_in_seconds)
|
197
206
|
rescue JSON::ParserError => e
|
198
207
|
Liri.logger.error("Exception(#{e}) Error de parseo JSON")
|
199
208
|
end
|
200
209
|
end
|
201
210
|
|
202
211
|
update_processing_statuses
|
203
|
-
|
204
|
-
Liri.logger.info("Se termina la conexión con el Agent #{agent_ip_address}")
|
212
|
+
Liri.logger.info("Se termina la conexión con el Agent #{agent_ip_address} en el puerto TCP: #{@tcp_port}")
|
205
213
|
begin
|
206
214
|
client.puts('exit') # Se envía el string exit para que el Agent sepa que el proceso terminó
|
207
215
|
client.close # se desconecta el cliente
|
@@ -215,44 +223,101 @@ module Liri
|
|
215
223
|
end
|
216
224
|
end
|
217
225
|
|
218
|
-
Liri.
|
219
|
-
@
|
226
|
+
Liri.clean_folder_content(@manager_folder_path)
|
227
|
+
@tests_result.print_summary
|
228
|
+
print_agents_summary
|
229
|
+
@tests_result.print_failures if Liri.print_failures
|
230
|
+
end
|
231
|
+
|
232
|
+
def all_tests
|
233
|
+
@semaphore.synchronize do
|
234
|
+
@all_tests
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def agents_search_processing_enabled=(value)
|
239
|
+
@semaphore.synchronize do
|
240
|
+
@agents_search_processing_enabled = value
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
def agents_search_processing_enabled
|
245
|
+
@semaphore.synchronize do
|
246
|
+
@agents_search_processing_enabled
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def test_processing_enabled
|
251
|
+
@semaphore.synchronize do
|
252
|
+
@test_processing_enabled
|
253
|
+
end
|
220
254
|
end
|
221
255
|
|
222
256
|
def update_processing_statuses
|
223
|
-
@semaphore.synchronize
|
257
|
+
@semaphore.synchronize do
|
224
258
|
@test_processing_enabled = false if @all_tests_count == @all_tests_results_count
|
225
259
|
@agents_search_processing_enabled = false if @all_tests_count == @all_tests_processing_count
|
226
|
-
|
260
|
+
end
|
227
261
|
end
|
228
262
|
|
229
|
-
def
|
230
|
-
|
231
|
-
|
263
|
+
def tests_batch(agent_ip_address)
|
264
|
+
# Se inicia un semáforo para evitar que varios hilos actualicen variables compartidas
|
265
|
+
@semaphore.synchronize do
|
266
|
+
return nil if @all_tests.empty?
|
232
267
|
|
233
|
-
|
234
|
-
|
268
|
+
@tests_batch_number += 1 # Se numera cada lote
|
269
|
+
samples = @all_tests.sample!(Manager.test_samples_by_runner) # Se obtiene algunos tests
|
270
|
+
samples_keys = samples.keys # Se obtiene la clave asignada a los tests
|
271
|
+
@all_tests_processing_count += samples_keys.size
|
272
|
+
|
273
|
+
@agents[agent_ip_address] = { agent_ip_address: agent_ip_address, tests_processed_count: 0, examples: 0, failures: 0, time_in_seconds: 0, duration: '' } unless @agents[agent_ip_address]
|
274
|
+
|
275
|
+
#@tests_batches[@tests_batch_number] = { agent_ip_address: agent_ip_address, tests_batch_keys: samples_keys } # Se guarda el lote a enviar
|
276
|
+
tests_batch = { tests_batch_number: @tests_batch_number, tests_batch_keys: samples_keys } # Se construye el lote a enviar
|
277
|
+
tests_batch
|
278
|
+
end
|
235
279
|
end
|
236
280
|
|
237
|
-
def
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
281
|
+
def process_tests_result(agent_ip_address, tests_result, time_in_seconds)
|
282
|
+
# Se inicia un semáforo para evitar que varios hilos actualicen variables compartidas
|
283
|
+
@semaphore.synchronize do
|
284
|
+
tests_batch_number = tests_result['tests_batch_number']
|
285
|
+
tests_result_file_name = tests_result['tests_result_file_name']
|
286
|
+
tests_batch_keys_size = tests_result['tests_batch_keys_size']
|
287
|
+
|
288
|
+
#tests_batch_keys = @tests_batches[tests_batch_number][:tests_batch_keys]
|
289
|
+
tests_processed_count = tests_batch_keys_size
|
290
|
+
@all_tests_results_count += tests_processed_count
|
291
|
+
|
292
|
+
@progressbar.progress = @all_tests_results_count
|
293
|
+
|
294
|
+
#@tests_batches[tests_batch_number][:tests_result_file_name] = tests_result_file_name
|
295
|
+
|
296
|
+
tests_result = @tests_result.process(tests_result_file_name)
|
297
|
+
|
298
|
+
@agents[agent_ip_address][:tests_processed_count] += tests_processed_count
|
299
|
+
@agents[agent_ip_address][:examples] += tests_result[:example_quantity]
|
300
|
+
@agents[agent_ip_address][:failures] += tests_result[:failure_quantity]
|
301
|
+
@agents[agent_ip_address][:time_in_seconds] += time_in_seconds
|
302
|
+
@agents[agent_ip_address][:duration] = @agents[agent_ip_address][:time_in_seconds].to_duration
|
303
|
+
|
304
|
+
Liri.logger.info("Pruebas procesadas por Agente: #{agent_ip_address}: #{@agents[agent_ip_address][:tests_processed_count]}")
|
305
|
+
end
|
247
306
|
end
|
248
307
|
|
249
|
-
def
|
250
|
-
|
251
|
-
@
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
308
|
+
def print_agents_summary
|
309
|
+
rows = @agents.values.map { |line| line.values }
|
310
|
+
headings = @agents.values.first.keys
|
311
|
+
|
312
|
+
table = Terminal::Table.new title: "Resúmen", headings: headings, rows: rows
|
313
|
+
table.style = {padding_left: 3, border_x: "=", border_i: "x" }
|
314
|
+
table.align_column(1, :right)
|
315
|
+
table.align_column(2, :right)
|
316
|
+
table.align_column(3, :right)
|
317
|
+
table.align_column(4, :right)
|
318
|
+
table.align_column(5, :right)
|
319
|
+
|
320
|
+
puts table
|
256
321
|
end
|
257
322
|
end
|
258
323
|
end
|
data/lib/task.rb
CHANGED
@@ -5,8 +5,8 @@
|
|
5
5
|
module Liri
|
6
6
|
module Task
|
7
7
|
class << self
|
8
|
-
def tests_count
|
9
|
-
source_code = Liri::Common::SourceCode.new('', Liri.compression_class, Liri.unit_test_class)
|
8
|
+
def tests_count(source_code_folder_path)
|
9
|
+
source_code = Liri::Common::SourceCode.new(source_code_folder_path,'', Liri.compression_class, Liri.unit_test_class)
|
10
10
|
source_code.all_tests.size
|
11
11
|
end
|
12
12
|
end
|