liri 0.2.1 → 0.4.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/.rubocop.yml +5 -1
- data/Gemfile +3 -0
- data/Gemfile.lock +67 -7
- data/README.md +3 -0
- data/Rakefile +5 -0
- data/bash_script_examples.sh +3 -1
- data/dummy-app/spec/dummy/dummy_spec.rb +11 -0
- data/exe/liri +26 -7
- data/lib/agent/agent.rb +120 -108
- data/lib/all_libraries.rb +5 -0
- data/lib/common/benchmarking.rb +11 -16
- data/lib/common/compressor/zip.rb +3 -2
- data/lib/common/duration.rb +28 -0
- data/lib/common/hardware.rb +29 -0
- data/lib/common/log.rb +59 -17
- data/lib/common/manager_data.rb +4 -4
- data/lib/common/progressbar.rb +3 -3
- data/lib/common/setup.rb +14 -4
- data/lib/common/source_code.rb +2 -2
- data/lib/common/tests_result.rb +72 -31
- data/lib/common/text_time_parser.rb +51 -0
- data/lib/common/tty_progressbar.rb +70 -0
- data/lib/common/unit_test/rspec.rb +6 -33
- data/lib/common/unit_test/rspec_result_parser.rb +47 -0
- data/lib/hash_extend.rb +15 -6
- data/lib/liri.rb +29 -21
- data/lib/manager/credential.rb +15 -1
- data/lib/manager/manager.rb +471 -165
- data/lib/task.rb +6 -2
- data/liri.gemspec +6 -6
- data/template/liri-config.yml +74 -30
- metadata +15 -10
- data/config/locales/to_duration_es.yml +0 -25
data/lib/manager/manager.rb
CHANGED
@@ -15,34 +15,86 @@ module Liri
|
|
15
15
|
def run(source_code_folder_path, stop = false)
|
16
16
|
return unless valid_project
|
17
17
|
|
18
|
-
setup_manager = Liri.set_setup(source_code_folder_path)
|
18
|
+
setup_manager = Liri.set_setup(source_code_folder_path, :manager, manager_tests_results_folder_time: DateTime.now.strftime("%d_%m_%y_%H_%M_%S"))
|
19
19
|
manager_folder_path = setup_manager.manager_folder_path
|
20
|
+
manager_tests_results_folder_path = setup_manager.manager_tests_results_folder_path
|
20
21
|
|
21
|
-
Liri.set_logger(setup_manager.logs_folder_path, '
|
22
|
-
Liri.logger.info(
|
23
|
-
Liri.logger.info("
|
22
|
+
Liri.set_logger(setup_manager.logs_folder_path, 'lirimanager.log')
|
23
|
+
Liri.logger.info('Manager process started', true)
|
24
|
+
Liri.logger.info("Press Ctrl + c to finish Manager process manually\n", true)
|
24
25
|
|
25
26
|
user, password = get_credentials(setup_manager.setup_folder_path)
|
26
27
|
source_code = compress_source_code(source_code_folder_path, manager_folder_path)
|
27
|
-
manager_data = get_manager_data(user, password,
|
28
|
+
manager_data = get_manager_data(user, password, manager_tests_results_folder_path, source_code)
|
28
29
|
all_tests = get_all_tests(source_code)
|
29
|
-
tests_result = Common::TestsResult.new(
|
30
|
+
tests_result = Common::TestsResult.new(manager_tests_results_folder_path)
|
30
31
|
|
31
|
-
manager = Manager.new(Liri.udp_port, Liri.tcp_port, all_tests, tests_result
|
32
|
+
manager = Manager.new(Liri.udp_port, Liri.tcp_port, all_tests, tests_result)
|
32
33
|
|
33
34
|
threads = []
|
34
35
|
threads << manager.start_client_socket_to_search_agents(manager_data) # Enviar peticiones broadcast a toda la red para encontrar Agents
|
35
|
-
|
36
|
+
unless stop
|
37
|
+
# Esperar y enviar los test unitarios a los Agents
|
38
|
+
manager.start_server_socket_to_process_tests(threads[0])
|
39
|
+
end
|
36
40
|
|
37
|
-
Liri.init_exit(stop, threads
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
41
|
+
Liri.init_exit(stop, threads)
|
42
|
+
rescue SignalException
|
43
|
+
# Liri.logger.info("\nManager process finished manually", true)
|
44
|
+
ensure
|
45
|
+
# Siempre se ejecutan estos comandos, haya o no excepción
|
46
|
+
Liri.kill(threads) if threads&.any?
|
47
|
+
manager&.print_results
|
48
|
+
source_code&.delete_compressed_file
|
49
|
+
Liri.logger.info("Manager process finished", true)
|
50
|
+
end
|
51
|
+
|
52
|
+
def udp_request_delay
|
53
|
+
Liri.setup.manager.udp_request_delay
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_files_by_runner
|
57
|
+
Liri.setup.manager.test_files_by_runner
|
58
|
+
end
|
59
|
+
|
60
|
+
def show_share_source_code_progress_bar
|
61
|
+
Liri.setup.manager.bar.share_source_code
|
42
62
|
end
|
43
63
|
|
44
|
-
def
|
45
|
-
Liri.setup.
|
64
|
+
def print_summary_table
|
65
|
+
Liri.setup.manager.print.table.summary
|
66
|
+
end
|
67
|
+
|
68
|
+
def print_detailed_table
|
69
|
+
Liri.setup.manager.print.table.detailed
|
70
|
+
end
|
71
|
+
|
72
|
+
def print_summary_failures
|
73
|
+
Liri.setup.manager.print.failures.summary
|
74
|
+
end
|
75
|
+
|
76
|
+
def print_detailed_failures
|
77
|
+
Liri.setup.manager.print.failures.detailed
|
78
|
+
end
|
79
|
+
|
80
|
+
def show_failed_files_column
|
81
|
+
Liri.setup.manager.print.column.failed_files
|
82
|
+
end
|
83
|
+
|
84
|
+
def show_files_load_column
|
85
|
+
Liri.setup.manager.print.column.files_load
|
86
|
+
end
|
87
|
+
|
88
|
+
def show_finish_in_column
|
89
|
+
Liri.setup.manager.print.column.finish_in
|
90
|
+
end
|
91
|
+
|
92
|
+
def show_batch_run_column
|
93
|
+
Liri.setup.manager.print.column.batch_run
|
94
|
+
end
|
95
|
+
|
96
|
+
def show_share_source_code_column
|
97
|
+
Liri.setup.manager.print.column.share_source_code
|
46
98
|
end
|
47
99
|
|
48
100
|
private
|
@@ -50,9 +102,9 @@ module Liri
|
|
50
102
|
if File.exist?(File.join(Dir.pwd, 'Gemfile'))
|
51
103
|
true
|
52
104
|
else
|
53
|
-
Liri.logger.info(
|
54
|
-
Liri.logger.info(
|
55
|
-
Liri.logger.info(
|
105
|
+
Liri.logger.info('Not found Gemfile. Assuming run Manager in not Ruby project')
|
106
|
+
Liri.logger.info('Liri can be run only in Ruby projects')
|
107
|
+
Liri.logger.info('Manager process finished')
|
56
108
|
false
|
57
109
|
end
|
58
110
|
end
|
@@ -63,19 +115,24 @@ module Liri
|
|
63
115
|
end
|
64
116
|
|
65
117
|
def compress_source_code(source_code_folder_path, manager_folder_path)
|
66
|
-
source_code = Common::SourceCode.new(source_code_folder_path, manager_folder_path, Liri.compression_class, Liri.unit_test_class)
|
67
|
-
|
68
|
-
Common::
|
118
|
+
source_code = Common::SourceCode.new(source_code_folder_path, manager_folder_path, Liri.ignored_folders_in_compress, Liri.compression_class, Liri.unit_test_class)
|
119
|
+
#Common::Progressbar.start(total: nil, length: 120, format: 'Compressing source code |%B| %a') do
|
120
|
+
Common::TtyProgressbar.start("Compressing source code |:bar| :percent | Time: :time", total: nil, width: 80, bar_format: :box) do
|
69
121
|
source_code.compress_folder
|
70
122
|
end
|
71
|
-
puts "\n
|
72
|
-
|
123
|
+
puts "\n"
|
124
|
+
Liri.logger.info("Batch Files: #{test_files_by_runner}", true)
|
125
|
+
puts "\n"
|
73
126
|
source_code
|
127
|
+
rescue SignalException => e
|
128
|
+
# Se captura la excepción sólo para imprimir espacios despues de la barra de progreso
|
129
|
+
puts "\n\n"
|
130
|
+
raise e
|
74
131
|
end
|
75
132
|
|
76
|
-
def get_manager_data(user, password,
|
133
|
+
def get_manager_data(user, password, tests_results_folder_path, source_code)
|
77
134
|
Common::ManagerData.new(
|
78
|
-
|
135
|
+
tests_results_folder_path: tests_results_folder_path,
|
79
136
|
compressed_file_path: source_code.compressed_file_path,
|
80
137
|
user: user,
|
81
138
|
password: password
|
@@ -85,39 +142,52 @@ module Liri
|
|
85
142
|
def get_all_tests(source_code)
|
86
143
|
all_tests = {}
|
87
144
|
|
88
|
-
Common::
|
145
|
+
#Common::TtyProgressbar.start("Getting unit tests |:bar| Time::elapsed", total: nil, width: 100) do
|
146
|
+
#Common::Progressbar.start(total: nil, length: 120, format: 'Getting unit tests |%B| %a') do
|
89
147
|
all_tests = source_code.all_tests
|
90
|
-
end
|
91
|
-
puts "\n\n"
|
148
|
+
#end
|
149
|
+
#puts "\n\n"
|
92
150
|
|
93
151
|
all_tests
|
152
|
+
rescue SignalException => e
|
153
|
+
# Se captura la excepción sólo para imprimir espacios despues de la barra de progreso
|
154
|
+
puts "\n\n"
|
155
|
+
raise e
|
94
156
|
end
|
95
157
|
end
|
96
158
|
|
97
|
-
def initialize(udp_port, tcp_port, all_tests, tests_result
|
159
|
+
def initialize(udp_port, tcp_port, all_tests, tests_result)
|
98
160
|
@udp_port = udp_port
|
99
161
|
@udp_socket = UDPSocket.new
|
100
162
|
@tcp_port = tcp_port
|
101
163
|
|
102
|
-
@
|
103
|
-
@all_tests_count = all_tests.size
|
104
|
-
@all_tests_results = {}
|
105
|
-
@all_tests_results_count = 0
|
106
|
-
@all_tests_processing_count = 0
|
107
|
-
@agents = {}
|
108
|
-
|
109
|
-
@agents_search_processing_enabled = true
|
110
|
-
@test_processing_enabled = true
|
111
|
-
|
112
|
-
@tests_batch_number = 0
|
164
|
+
@batch_num = 0
|
113
165
|
@tests_batches = {}
|
166
|
+
@tests_files_count = 0
|
167
|
+
build_tests_batches(all_tests)
|
168
|
+
|
169
|
+
@files_processed = 0
|
170
|
+
@agents = {}
|
171
|
+
@connected_agents = {}
|
172
|
+
@working_agents = {}
|
114
173
|
|
115
174
|
@tests_result = tests_result
|
116
175
|
@semaphore = Mutex.new
|
117
176
|
|
118
|
-
@
|
177
|
+
@tests_processing_bar = TTY::ProgressBar::Multi.new("Tests Running Progress")
|
178
|
+
@tests_running_progress_bar = @tests_processing_bar.register("Tests files processed :current/:total |:bar| :percent | Time: :time", total: @tests_files_count, width: 80, bar_format: :box)
|
179
|
+
@agents_bar = @tests_processing_bar.register("Agents: Connected: :connected, Working: :working")
|
180
|
+
@tests_result_bar = @tests_processing_bar.register("Examples: :examples, Passed: :passed, Failures: :failures")
|
181
|
+
|
182
|
+
@tests_processing_bar.start # Se inicia la multi barra de progreso
|
183
|
+
|
184
|
+
# Se establece el estado inicial de las barras
|
185
|
+
@tests_running_progress_bar.use(Common::TtyProgressbar::TimeFormatter) # Se configura el uso de un nuevo token llamado time para mostrar el tiempo de ejcución
|
186
|
+
@tests_running_progress_bar.advance(0) # Esto obliga a que esta barra se muestre antes que los siguientes
|
187
|
+
@tests_running_progress_bar.pause
|
119
188
|
|
120
|
-
@
|
189
|
+
@agents_bar.advance(0, connected: "0", working: "0")
|
190
|
+
@tests_result_bar.advance(0, examples: "0", passed: "0", failures: "0")
|
121
191
|
end
|
122
192
|
|
123
193
|
# Inicia un cliente udp que hace un broadcast en toda la red para iniciar una conexión con los Agent que estén escuchando
|
@@ -125,14 +195,12 @@ module Liri
|
|
125
195
|
# El cliente udp se ejecuta en bucle dentro de un hilo, esto permite realizar otras tareas mientras este hilo sigue sondeando
|
126
196
|
# la red para obtener mas Agents. Una vez que los tests terminan de ejecutarse, este hilo será finalizado.
|
127
197
|
Thread.new do
|
128
|
-
Liri.logger.info(
|
129
|
-
Liri.logger.info("
|
130
|
-
|
131
|
-
")
|
132
|
-
while agents_search_processing_enabled
|
198
|
+
Liri.logger.info('Searching agents... Wait')
|
199
|
+
Liri.logger.info("Sending UDP broadcast each #{Manager.udp_request_delay} seconds in UDP port: #{@udp_port}")
|
200
|
+
while processing
|
133
201
|
@udp_socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_BROADCAST, true)
|
134
202
|
@udp_socket.send(manager_data.to_h.to_json, 0, '<broadcast>', @udp_port)
|
135
|
-
sleep(
|
203
|
+
sleep(Manager.udp_request_delay) # Se pausa un momento antes de efectuar nuevamente la petición broadcast
|
136
204
|
end
|
137
205
|
end
|
138
206
|
end
|
@@ -142,179 +210,417 @@ module Liri
|
|
142
210
|
begin
|
143
211
|
tcp_socket = TCPServer.new(@tcp_port) # se hace un bind al puerto dado
|
144
212
|
rescue Errno::EADDRINUSE => e
|
145
|
-
Liri.logger.error("Exception(#{e})
|
213
|
+
Liri.logger.error("Exception(#{e}) Busy UDP port #{@tcp_port}.")
|
146
214
|
Thread.kill(search_agents_thread)
|
147
|
-
|
215
|
+
return
|
148
216
|
end
|
149
217
|
|
150
|
-
Liri.logger.info("
|
151
|
-
(Se espera que algún Agent se conecte para ejecutar las pruebas como respuesta al broadcast UDP)
|
152
|
-
")
|
218
|
+
Liri.logger.info("Waiting Agents connection in TCP port: #{@tcp_port}")
|
153
219
|
# El siguiente bucle permite que varios clientes es decir Agents se conecten
|
154
220
|
# De: http://www.w3big.com/es/ruby/ruby-socket-programming.html
|
155
|
-
while
|
221
|
+
while processing
|
156
222
|
Thread.start(tcp_socket.accept) do |client|
|
157
223
|
agent_ip_address = client.remote_address.ip_address
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
224
|
+
hardware_specs = nil
|
225
|
+
run_tests_batch_time_start = nil
|
226
|
+
share_source_code_time_start = nil
|
227
|
+
share_source_code_progress_bar = nil
|
228
|
+
|
229
|
+
while line = client.gets
|
230
|
+
client_data = JSON.parse(line.chop)
|
231
|
+
msg = client_data['msg']
|
232
|
+
|
233
|
+
if msg == 'get_source_code'
|
234
|
+
if registered_agent?(agent_ip_address)
|
235
|
+
client.puts({ msg: 'already_connected' }.to_json)
|
236
|
+
client.close
|
237
|
+
break
|
238
|
+
else
|
239
|
+
register_agent(agent_ip_address)
|
240
|
+
update_connected_agents(agent_ip_address)
|
241
|
+
hardware_specs = client_data['hardware_specs']
|
242
|
+
msg = processing ? 'proceed_get_source_code' : 'no_exist_tests'
|
243
|
+
share_source_code_time_start = Time.now
|
244
|
+
|
245
|
+
share_source_code_progress_bar = start_share_source_code_progress_bar(hardware_specs, msg)
|
179
246
|
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
break unless tests_batch
|
247
|
+
client.puts({ msg: msg }.to_json)
|
248
|
+
end
|
249
|
+
end
|
184
250
|
|
185
|
-
|
186
|
-
|
251
|
+
if msg == 'get_source_code_fail'
|
252
|
+
stop_share_source_code_progress_bar(hardware_specs, share_source_code_progress_bar)
|
187
253
|
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
254
|
+
client.puts({ msg: 'finish_agent' }.to_json)
|
255
|
+
client.close
|
256
|
+
break
|
257
|
+
end
|
258
|
+
|
259
|
+
# Primera ejecucion de pruebas
|
260
|
+
if msg == 'get_tests_files'
|
261
|
+
stop_share_source_code_progress_bar(hardware_specs, share_source_code_progress_bar)
|
262
|
+
|
263
|
+
share_source_code_time_end = Time.now - share_source_code_time_start
|
264
|
+
|
265
|
+
Liri.logger.info("Running unit tests. Agent: #{agent_ip_address}. Wait... ", false)
|
266
|
+
|
267
|
+
start_tests_running_progress_bar
|
268
|
+
run_tests_batch_time_start = Time.now
|
269
|
+
update_working_agents(agent_ip_address)
|
270
|
+
tests_batch = tests_batch(agent_ip_address, hardware_specs, share_source_code_time_end)
|
271
|
+
|
272
|
+
if tests_batch.empty?
|
273
|
+
client.puts({ msg: 'no_exist_tests' }.to_json)
|
274
|
+
client.close
|
194
275
|
break
|
276
|
+
else
|
277
|
+
client.puts(tests_batch.to_json) # Se envia el lote de tests
|
195
278
|
end
|
196
279
|
end
|
197
280
|
|
198
|
-
#
|
199
|
-
|
200
|
-
tests_result =
|
201
|
-
Liri.logger.debug("
|
202
|
-
process_tests_result(agent_ip_address, tests_result,
|
203
|
-
|
204
|
-
|
281
|
+
# Segunda ejecucion de pruebas y las siguientes ejecuciones
|
282
|
+
if msg == 'processed_tests'
|
283
|
+
tests_result = client_data
|
284
|
+
Liri.logger.debug("Agent response #{agent_ip_address}: #{tests_result}")
|
285
|
+
process_tests_result(agent_ip_address, hardware_specs, tests_result, run_tests_batch_time_start)
|
286
|
+
|
287
|
+
run_tests_batch_time_start = Time.now
|
288
|
+
|
289
|
+
tests_batch = tests_batch(agent_ip_address, hardware_specs, 0)
|
290
|
+
if tests_batch.empty?
|
291
|
+
client.puts({ msg: 'no_exist_tests' }.to_json)
|
292
|
+
client.close
|
293
|
+
break
|
294
|
+
else
|
295
|
+
client.puts(tests_batch.to_json) # Se envia el lote de tests
|
296
|
+
end
|
205
297
|
end
|
206
298
|
end
|
207
299
|
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
Liri.logger.error("Exception(#{e}) El Agent #{agent_ip_address} ya terminó la conexión")
|
216
|
-
# Si el Agente ya no responde es mejor terminar el hilo. Aunque igual quedará colgado el Manager
|
217
|
-
# mientras sigan pruebas pendientes
|
218
|
-
Thread.exit
|
219
|
-
end
|
300
|
+
Thread.kill(search_agents_thread)
|
301
|
+
rescue Errno::EPIPE => e
|
302
|
+
# Esto al parecer se da cuando el Agent ya cerró las conexiones y el Manager intenta contactar
|
303
|
+
Liri.logger.error("Exception(#{e}) Agent #{agent_ip_address} already finished connection")
|
304
|
+
# Si el Agente ya no responde es mejor terminar el hilo. Aunque igual quedará colgado el Manager
|
305
|
+
# mientras sigan pruebas pendientes
|
306
|
+
Thread.exit
|
220
307
|
end
|
221
308
|
end
|
222
|
-
|
223
|
-
Liri.clean_folder_content(@manager_folder_path)
|
224
|
-
@tests_result.print_summary
|
225
|
-
print_agents_summary
|
226
|
-
@tests_result.print_failures if Liri.print_failures
|
227
309
|
end
|
228
310
|
|
229
|
-
def
|
311
|
+
def processing
|
230
312
|
@semaphore.synchronize do
|
231
|
-
@
|
313
|
+
@unfinished_tests_batches.positive?
|
232
314
|
end
|
233
315
|
end
|
234
316
|
|
235
|
-
def
|
236
|
-
|
237
|
-
@
|
317
|
+
def build_tests_batches(all_tests)
|
318
|
+
while all_tests.any?
|
319
|
+
@batch_num += 1 # Se numera cada lote
|
320
|
+
samples = all_tests.sample!(Manager.test_files_by_runner) # Se obtiene algunos tests
|
321
|
+
samples_keys = samples.keys # Se obtiene la clave asignada a los tests
|
322
|
+
files_count = samples.size
|
323
|
+
status = "pending"
|
324
|
+
@tests_files_count += files_count
|
325
|
+
# Se construye el lote a enviar
|
326
|
+
tests_batch = {
|
327
|
+
batch_num: @batch_num,
|
328
|
+
tests_batch_keys: samples_keys,
|
329
|
+
msg: "process_tests",
|
330
|
+
files_count: files_count,
|
331
|
+
status: status,
|
332
|
+
files_status: "#{files_count} #{status}",
|
333
|
+
agent_ip_address: "",
|
334
|
+
examples: 0,
|
335
|
+
passed: 0,
|
336
|
+
failures: 0,
|
337
|
+
pending: 0,
|
338
|
+
failed_files: "",
|
339
|
+
files_load: 0,
|
340
|
+
finish_in: 0,
|
341
|
+
batch_run: 0,
|
342
|
+
share_source_code: 0,
|
343
|
+
tests_runtime: 0,
|
344
|
+
hardware_specs: ""
|
345
|
+
}
|
346
|
+
@tests_batches[@batch_num] = tests_batch
|
238
347
|
end
|
348
|
+
|
349
|
+
@unfinished_tests_batches = @batch_num
|
239
350
|
end
|
240
351
|
|
241
|
-
def
|
352
|
+
def tests_batch(agent_ip_address, hardware_specs, share_source_code_time_end)
|
353
|
+
# Se inicia un semáforo para evitar que varios hilos actualicen variables compartidas
|
242
354
|
@semaphore.synchronize do
|
243
|
-
@
|
355
|
+
return {} if @unfinished_tests_batches.zero?
|
356
|
+
|
357
|
+
tests_batch = {}
|
358
|
+
pending_tests_batch = {}
|
359
|
+
sent_tests_batch = {}
|
360
|
+
|
361
|
+
@tests_batches.each_value do |batch|
|
362
|
+
if batch[:status] == "pending"
|
363
|
+
pending_tests_batch = batch
|
364
|
+
break
|
365
|
+
elsif batch[:status] == "sent"
|
366
|
+
sent_tests_batch = batch # Es importante que este no tenga un break para guardar el ultimo enviado
|
367
|
+
# el cual tiene menos probabilidades de terminar de ejecutarse rapido
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
# Es importante setear el status y el hardware_spec solo si los hashes no estan vacios
|
372
|
+
# Porque si estan vacios significa que ya no hay tests que ejecutar, y si seteamos algun valor en el hash
|
373
|
+
# estando este vacio entonces se tratara de ejecutar algo sin los datos suficientes y fallara
|
374
|
+
if pending_tests_batch.any?
|
375
|
+
tests_batch = pending_tests_batch
|
376
|
+
tests_batch[:status] = "sent"
|
377
|
+
tests_batch[:agent_ip_address] = agent_ip_address
|
378
|
+
tests_batch[:hardware_specs] = hardware_specs
|
379
|
+
elsif sent_tests_batch.any?
|
380
|
+
tests_batch = sent_tests_batch
|
381
|
+
tests_batch[:status] = "resent"
|
382
|
+
tests_batch[:agent_ip_address] = agent_ip_address
|
383
|
+
tests_batch[:hardware_specs] = hardware_specs
|
384
|
+
end
|
385
|
+
|
386
|
+
return {} if tests_batch.empty?
|
387
|
+
|
388
|
+
tests_batch[:agent_ip_address] = agent_ip_address
|
389
|
+
tests_batch[:share_source_code] = share_source_code_time_end
|
390
|
+
|
391
|
+
Liri.logger.debug("Tests batches sent to Agent #{agent_ip_address}: #{tests_batch}")
|
392
|
+
# se devuelve el hash con los datos que se enviarán al agente, por eso, primero se remueven los datos innecesarios
|
393
|
+
tests_batch.remove(:files_count, :status, :files_status, :agent_ip_address, :examples, :passed, :failures,
|
394
|
+
:pending, :failed_files, :files_load, :finish_in, :batch_run, :share_source_code,
|
395
|
+
:tests_runtime, :hardware_specs)
|
244
396
|
end
|
245
397
|
end
|
246
398
|
|
247
|
-
def
|
399
|
+
def process_tests_result(agent_ip_address, hardware_specs, tests_result, run_tests_batch_time_start)
|
400
|
+
# Se inicia un semáforo para evitar que varios hilos actualicen variables compartidas
|
248
401
|
@semaphore.synchronize do
|
249
|
-
|
402
|
+
batch_num = tests_result['batch_num']
|
403
|
+
tests_result_file_name = tests_result['tests_result_file_name']
|
404
|
+
status = "processed"
|
405
|
+
# Sólo se procesan las pruebas en estado sent o resent, caso contrario no se avanza con el procesamiento
|
406
|
+
return if (["pending", status]).include?(@tests_batches[batch_num][:status])
|
407
|
+
|
408
|
+
tests_result = @tests_result.process(tests_result_file_name)
|
409
|
+
return if tests_result.empty?
|
410
|
+
|
411
|
+
@unfinished_tests_batches -= 1
|
412
|
+
|
413
|
+
files_count = @tests_batches[batch_num][:files_count]
|
414
|
+
@files_processed += files_count
|
415
|
+
|
416
|
+
batch_runtime = Time.now - run_tests_batch_time_start
|
417
|
+
|
418
|
+
@tests_running_progress_bar.advance(files_count)
|
419
|
+
@tests_result_bar.advance(1, examples: @tests_result.examples.to_s, passed: @tests_result.passed.to_s, failures: @tests_result.failures.to_s)
|
420
|
+
@tests_running_progress_bar.stop if @unfinished_tests_batches.zero?
|
421
|
+
|
422
|
+
@tests_batches[batch_num][:status] = status
|
423
|
+
@tests_batches[batch_num][:files_status] = "#{files_count} #{status}"
|
424
|
+
@tests_batches[batch_num][:agent_ip_address] = agent_ip_address
|
425
|
+
@tests_batches[batch_num][:examples] = tests_result[:examples]
|
426
|
+
@tests_batches[batch_num][:passed] = tests_result[:passed]
|
427
|
+
@tests_batches[batch_num][:failures] = tests_result[:failures]
|
428
|
+
@tests_batches[batch_num][:pending] = tests_result[:pending]
|
429
|
+
@tests_batches[batch_num][:failed_files] = tests_result[:failed_files]
|
430
|
+
@tests_batches[batch_num][:files_load] = tests_result[:files_load]
|
431
|
+
@tests_batches[batch_num][:finish_in] = tests_result[:finish_in]
|
432
|
+
@tests_batches[batch_num][:batch_run] = batch_runtime
|
433
|
+
@tests_batches[batch_num][:tests_runtime] = @tests_batches[batch_num][:batch_run] + @tests_batches[batch_num][:share_source_code]
|
434
|
+
@tests_batches[batch_num][:hardware_specs] = hardware_specs
|
435
|
+
|
436
|
+
Liri.logger.info("Processed unit tests by Agent: #{agent_ip_address}: #{files_count}")
|
250
437
|
end
|
251
438
|
end
|
252
439
|
|
253
|
-
def
|
254
|
-
@
|
255
|
-
|
256
|
-
|
440
|
+
def print_results
|
441
|
+
@tests_processing_bar&.stop
|
442
|
+
print_summary_table if Manager.print_summary_table
|
443
|
+
print_detailed_table if Manager.print_detailed_table
|
444
|
+
@tests_result.print_summary_failures if Manager.print_summary_failures
|
445
|
+
@tests_result.print_detailed_failures if Manager.print_detailed_failures
|
446
|
+
end
|
447
|
+
|
448
|
+
def print_summary_table
|
449
|
+
processed_tests_batches_by_agent = processed_tests_batches_by_agents
|
450
|
+
rows = processed_tests_batches_by_agent.values.map do |value|
|
451
|
+
value[:files_load] = to_duration(value[:files_load]) if value[:files_load]
|
452
|
+
value[:finish_in] = to_duration(value[:finish_in]) if value[:finish_in]
|
453
|
+
value[:batch_run] = to_duration(value[:batch_run]) if value[:batch_run]
|
454
|
+
value[:share_source_code] = to_duration(value[:share_source_code]) if value[:share_source_code]
|
455
|
+
value[:tests_runtime] = to_duration(value[:tests_runtime]) if value[:tests_runtime]
|
456
|
+
value.values
|
257
457
|
end
|
458
|
+
|
459
|
+
rows << Array.new(rows.size) # Se agrega una linea vacia antes de mostrar los totales
|
460
|
+
rows << summary_footer.remove(:batch_num).values
|
461
|
+
header = processed_tests_batches_by_agent.values.first.keys
|
462
|
+
|
463
|
+
table = Terminal::Table.new title: 'Summary', headings: header, rows: rows
|
464
|
+
table.style = { padding_left: 3, border_x: '=', border_i: 'x'}
|
465
|
+
|
466
|
+
Liri.logger.info("\n#{table}", true)
|
258
467
|
end
|
259
468
|
|
260
|
-
def
|
261
|
-
|
262
|
-
|
263
|
-
|
469
|
+
def processed_tests_batches_by_agents
|
470
|
+
tests_batches = {}
|
471
|
+
files_count = {}
|
472
|
+
@tests_batches.each_value do |processed_test_batch|
|
473
|
+
agent_ip_address = processed_test_batch[:agent_ip_address]
|
474
|
+
status = processed_test_batch[:status]
|
475
|
+
key = "#{agent_ip_address}#{status}"
|
476
|
+
if tests_batches[key]
|
477
|
+
files_count[key] += processed_test_batch[:files_count]
|
478
|
+
tests_batches[key][:files_status] = "#{files_count[key]} #{status}"
|
479
|
+
tests_batches[key][:examples] += processed_test_batch[:examples]
|
480
|
+
tests_batches[key][:passed] += processed_test_batch[:passed]
|
481
|
+
tests_batches[key][:failures] += processed_test_batch[:failures]
|
482
|
+
tests_batches[key][:failed_files] += processed_test_batch[:failed_files] if Manager.show_failed_files_column
|
483
|
+
tests_batches[key][:files_load] += processed_test_batch[:files_load] if Manager.show_files_load_column
|
484
|
+
tests_batches[key][:finish_in] += processed_test_batch[:finish_in] if Manager.show_finish_in_column
|
485
|
+
tests_batches[key][:batch_run] += processed_test_batch[:batch_run] if Manager.show_batch_run_column
|
486
|
+
tests_batches[key][:share_source_code] += processed_test_batch[:share_source_code] if Manager.show_share_source_code_column
|
487
|
+
tests_batches[key][:tests_runtime] += processed_test_batch[:tests_runtime]
|
488
|
+
else
|
489
|
+
files_count[key] = processed_test_batch[:files_count]
|
264
490
|
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
@all_tests_processing_count += samples_keys.size
|
491
|
+
_processed_test_batch = processed_test_batch.clone # Clone to change values in other hash
|
492
|
+
_processed_test_batch.remove!(:batch_num, :tests_batch_keys, :msg, :files_count, :status, :agent_ip_address,
|
493
|
+
:pending)
|
269
494
|
|
270
|
-
|
495
|
+
_processed_test_batch.remove!(:failed_files) unless Manager.show_failed_files_column
|
496
|
+
_processed_test_batch.remove!(:files_load) unless Manager.show_files_load_column
|
497
|
+
_processed_test_batch.remove!(:finish_in) unless Manager.show_finish_in_column
|
498
|
+
_processed_test_batch.remove!(:batch_run) unless Manager.show_batch_run_column
|
499
|
+
_processed_test_batch.remove!(:share_source_code) unless Manager.show_share_source_code_column
|
500
|
+
_processed_test_batch[:files_status] = "#{files_count[key]} #{status}"
|
271
501
|
|
272
|
-
|
273
|
-
|
274
|
-
tests_batch
|
502
|
+
tests_batches[key] = _processed_test_batch
|
503
|
+
end
|
275
504
|
end
|
505
|
+
tests_batches
|
276
506
|
end
|
277
507
|
|
278
|
-
def
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
508
|
+
def print_detailed_table
|
509
|
+
rows = @tests_batches.values.map do |value|
|
510
|
+
value.remove!(:tests_batch_keys, :msg, :files_count, :status, :agent_ip_address, :pending)
|
511
|
+
|
512
|
+
value.remove!(:failed_files) unless Manager.show_failed_files_column
|
513
|
+
value.remove!(:files_load) unless Manager.show_files_load_column
|
514
|
+
value.remove!(:finish_in) unless Manager.show_finish_in_column
|
515
|
+
value.remove!(:batch_run) unless Manager.show_batch_run_column
|
516
|
+
value.remove!(:share_source_code) unless Manager.show_share_source_code_column
|
517
|
+
|
518
|
+
value[:files_load] = to_duration(value[:files_load]) if value[:files_load]
|
519
|
+
value[:finish_in] = to_duration(value[:finish_in]) if value[:finish_in]
|
520
|
+
value[:batch_run] = to_duration(value[:batch_run]) if value[:batch_run]
|
521
|
+
value[:share_source_code] = to_duration(value[:share_source_code]) if value[:share_source_code]
|
522
|
+
value[:tests_runtime] = to_duration(value[:tests_runtime])
|
523
|
+
value.values
|
524
|
+
end
|
284
525
|
|
285
|
-
|
286
|
-
|
287
|
-
|
526
|
+
rows << Array.new(rows.size) # Se agrega una linea vacia antes de mostrar los totales
|
527
|
+
rows << summary_footer.values
|
528
|
+
header = @tests_batches.values.first.keys
|
288
529
|
|
289
|
-
|
530
|
+
table = Terminal::Table.new title: 'Detailed Summary', headings: header, rows: rows
|
531
|
+
table.style = { padding_left: 3, border_x: '=', border_i: 'x' }
|
290
532
|
|
291
|
-
|
533
|
+
Liri.logger.info("\n#{table}", true)
|
534
|
+
end
|
292
535
|
|
293
|
-
|
536
|
+
def summary_footer
|
537
|
+
hash = {}
|
538
|
+
hash[:batch_num] = ""
|
539
|
+
hash[:files_status] = "#{@tests_files_count} in total"
|
540
|
+
hash[:examples] = @tests_result.examples
|
541
|
+
hash[:passed] = @tests_result.passed
|
542
|
+
hash[:failures] = @tests_result.failures
|
543
|
+
hash[:failed_files] = "" if Manager.show_failed_files_column
|
544
|
+
hash[:files_load] = "" if Manager.show_files_load_column
|
545
|
+
hash[:finish_in] = "" if Manager.show_finish_in_column
|
546
|
+
hash[:batch_run] = "" if Manager.show_batch_run_column
|
547
|
+
hash[:share_source_code] = "" if Manager.show_share_source_code_column
|
548
|
+
hash[:tests_runtime] = ""
|
549
|
+
hash[:hardware_specs] = ""
|
550
|
+
hash
|
551
|
+
end
|
552
|
+
|
553
|
+
def registered_agent?(agent_ip_address)
|
554
|
+
@agents[agent_ip_address]
|
555
|
+
end
|
556
|
+
|
557
|
+
def register_agent(agent_ip_address)
|
558
|
+
@agents[agent_ip_address] = agent_ip_address
|
559
|
+
Liri.logger.info("\nStarted connection with Agent: #{agent_ip_address} in TCP port: #{@tcp_port}")
|
560
|
+
end
|
561
|
+
|
562
|
+
def update_connected_agents(agent_ip_address)
|
563
|
+
unless @connected_agents[agent_ip_address]
|
564
|
+
@connected_agents[agent_ip_address] = agent_ip_address
|
565
|
+
update_agents_bar
|
566
|
+
end
|
567
|
+
end
|
294
568
|
|
295
|
-
|
296
|
-
|
297
|
-
@
|
298
|
-
|
299
|
-
|
569
|
+
def update_working_agents(agent_ip_address)
|
570
|
+
unless @working_agents[agent_ip_address]
|
571
|
+
@working_agents[agent_ip_address] = agent_ip_address
|
572
|
+
update_agents_bar
|
573
|
+
end
|
574
|
+
end
|
300
575
|
|
301
|
-
|
576
|
+
def update_agents_bar
|
577
|
+
@agents_bar.advance(1, connected: @connected_agents.size.to_s, working: @working_agents.size.to_s)
|
578
|
+
end
|
579
|
+
|
580
|
+
def start_share_source_code_progress_bar(hardware_specs, msg)
|
581
|
+
if msg == 'proceed_get_source_code' && Manager.show_share_source_code_progress_bar
|
582
|
+
share_source_code_progress_bar = @tests_processing_bar.register("Sharing source code |:bar| :percent | Time: :time | Agent: [:agent ]", total: nil, width: 20, bar_format: :box)
|
583
|
+
share_source_code_progress_bar.start
|
584
|
+
share_source_code_progress_bar.use(Common::TtyProgressbar::TimeFormatter)
|
585
|
+
Thread.new do
|
586
|
+
animation_count = 0
|
587
|
+
while !share_source_code_progress_bar.stopped?
|
588
|
+
share_source_code_progress_bar.advance(1, agent: hardware_specs)
|
589
|
+
|
590
|
+
share_source_code_progress_bar.update(unknown: Common::TtyProgressbar::ANIMATION2[animation_count])
|
591
|
+
animation_count += 1
|
592
|
+
animation_count = 0 if animation_count == 3
|
593
|
+
|
594
|
+
sleep(0.1)
|
595
|
+
end
|
596
|
+
end
|
302
597
|
end
|
598
|
+
share_source_code_progress_bar
|
303
599
|
end
|
304
600
|
|
305
|
-
def
|
306
|
-
|
307
|
-
|
601
|
+
def stop_share_source_code_progress_bar(hardware_specs, share_source_code_progress_bar)
|
602
|
+
if Manager.show_share_source_code_progress_bar
|
603
|
+
share_source_code_progress_bar.update(total: 1, agent: hardware_specs)
|
604
|
+
share_source_code_progress_bar.stop
|
605
|
+
end
|
606
|
+
end
|
308
607
|
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
608
|
+
def start_tests_running_progress_bar
|
609
|
+
@semaphore.synchronize do
|
610
|
+
# Es importante hacer un reset acá osino va a contar desde que se instancia y no desde que se inicia la ejecución
|
611
|
+
# del primer test. Solo se resetea si esta paused para evitar que al conectarse con cada Agent se vuelva a resetear
|
612
|
+
@tests_running_progress_bar.reset if @tests_running_progress_bar.paused?
|
613
|
+
Thread.new do
|
614
|
+
while !@tests_running_progress_bar.stopped?
|
615
|
+
@tests_running_progress_bar.advance(0)
|
616
|
+
sleep(0.1) # Es importante que las otras barras tambien tengan el mismo sleep para que sean mas consistentes en sus resultados
|
617
|
+
end
|
618
|
+
end
|
619
|
+
end
|
620
|
+
end
|
316
621
|
|
317
|
-
|
622
|
+
def to_duration(value)
|
623
|
+
Common::Duration.humanize(value, times_round: Liri.times_round, times_round_type: Liri.times_round_type)
|
318
624
|
end
|
319
625
|
end
|
320
626
|
end
|