liri 0.1.0 → 0.2.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 +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
|