liri 0.2.1 → 0.4.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/.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/all_libraries.rb
CHANGED
@@ -19,14 +19,19 @@ require 'agent/agent'
|
|
19
19
|
require 'agent/runner'
|
20
20
|
|
21
21
|
require 'common/benchmarking'
|
22
|
+
require 'common/duration'
|
23
|
+
require 'common/hardware'
|
22
24
|
require 'common/log'
|
23
25
|
require 'common/manager_data'
|
24
26
|
require 'common/progressbar'
|
27
|
+
require 'common/tty_progressbar'
|
25
28
|
require 'common/setup'
|
26
29
|
require 'common/source_code'
|
27
30
|
require 'common/compressor/zip'
|
28
31
|
require 'common/unit_test/rspec'
|
32
|
+
require 'common/unit_test/rspec_result_parser'
|
29
33
|
require 'common/tests_result'
|
34
|
+
require 'common/text_time_parser'
|
30
35
|
|
31
36
|
require 'manager/manager'
|
32
37
|
require 'manager/credential'
|
data/lib/common/benchmarking.rb
CHANGED
@@ -1,31 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# = benchmarking.rb
|
2
4
|
#
|
3
5
|
# @author Rodrigo Fernández
|
4
|
-
#
|
5
|
-
# == Módulo Benchmarking
|
6
|
-
# Este módulo se encarga de medir el tiempo de ejecución de algunos bloques de código
|
7
|
-
|
8
|
-
require 'benchmark'
|
9
|
-
require 'i18n' # requerimiento de la gema to_duration
|
10
|
-
require 'to_duration'
|
11
6
|
|
12
|
-
|
13
|
-
I18n.load_path << Dir[File.join(File.dirname(File.dirname(File.dirname(__FILE__))), 'config/locales') + "/*.yml"]
|
14
|
-
I18n.default_locale = :es
|
7
|
+
require "benchmark"
|
15
8
|
|
16
9
|
module Liri
|
17
10
|
module Common
|
11
|
+
# == Módulo Benchmarking
|
12
|
+
# Este módulo se encarga de medir el tiempo de ejecución de algunos bloques de código
|
18
13
|
module Benchmarking
|
19
14
|
class << self
|
20
|
-
def start(start_msg: nil, end_msg: '
|
15
|
+
def start(start_msg: nil, end_msg: 'Duration: ', stdout: true, &block)
|
21
16
|
Liri.logger.info(start_msg, stdout)
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
Liri.logger.info("#{end_msg}#{seconds.
|
17
|
+
|
18
|
+
seconds = Benchmark.realtime(&block)
|
19
|
+
|
20
|
+
Liri.logger.info("#{end_msg}#{Duration.humanize(seconds, times_round: Liri.times_round, times_round_type: Liri.times_round_type)}", stdout)
|
26
21
|
seconds
|
27
22
|
end
|
28
23
|
end
|
29
24
|
end
|
30
25
|
end
|
31
|
-
end
|
26
|
+
end
|
@@ -8,15 +8,16 @@ module Liri
|
|
8
8
|
module Compressor
|
9
9
|
class Zip
|
10
10
|
# Inicializa la carpeta a comprimir y la ubicación en donde se guardará el archivo comprimido
|
11
|
-
def initialize(input_dir, output_file)
|
11
|
+
def initialize(input_dir, output_file, ignored_folders)
|
12
12
|
@input_dir = input_dir
|
13
13
|
@output_file = output_file
|
14
|
+
@ignored_folders = ignored_folders.split(",")
|
14
15
|
end
|
15
16
|
|
16
17
|
# Comprime el directorio de entrada @input_dir en un archivo con extensión zip.
|
17
18
|
def compress
|
18
19
|
clear_output_file
|
19
|
-
entries = Dir.entries(@input_dir) - %w[. ..]
|
20
|
+
entries = Dir.entries(@input_dir) - (%w[. ..] + @ignored_folders)
|
20
21
|
|
21
22
|
::Zip::File.open(@output_file, ::Zip::File::CREATE) do |zipfile|
|
22
23
|
write_entries(entries, '', zipfile)
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# = duration.rb
|
4
|
+
#
|
5
|
+
# @author Rodrigo Fernández
|
6
|
+
|
7
|
+
require "chronic_duration"
|
8
|
+
|
9
|
+
module Liri
|
10
|
+
module Common
|
11
|
+
# == Módulo Duration
|
12
|
+
# Este módulo se encarga de convertir el tiempo en segundos a un formato legible
|
13
|
+
module Duration
|
14
|
+
class << self
|
15
|
+
def humanize(time, times_round:, times_round_type:)
|
16
|
+
# El time puede ser un BigDecimal y aunque se redondee puede responder con un formato 0.744e2, por eso
|
17
|
+
# es imporantes hacerle un to_f para convertirlo a 74.4 antes de proceder a humanizarlo
|
18
|
+
time = time.to_f
|
19
|
+
case times_round_type
|
20
|
+
when :floor then ChronicDuration.output(time.truncate(times_round), format: :short, keep_zero: true)
|
21
|
+
when :roof then ChronicDuration.output(time.round(times_round), format: :short, keep_zero: true)
|
22
|
+
else raise "Invalid times_round_type. Expected: floor or roof. Received: #{times_round_type}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# = hardware.rb
|
2
|
+
#
|
3
|
+
# @author Rodrigo Fernández
|
4
|
+
|
5
|
+
module Liri
|
6
|
+
module Common
|
7
|
+
# == Módulo Hardware
|
8
|
+
# Este módulo se encarga de obtener información del hardware
|
9
|
+
module Hardware
|
10
|
+
class << self
|
11
|
+
def cpu
|
12
|
+
cpu = %x|inxi -C|
|
13
|
+
cpu = cpu.to_s.match(/model(.+)bits/)
|
14
|
+
cpu = cpu[1].gsub("12", "")
|
15
|
+
cpu = cpu.gsub(":", "")
|
16
|
+
cpu.strip
|
17
|
+
rescue Errno::ENOENT
|
18
|
+
raise InxiCommandNotFoundError.new
|
19
|
+
end
|
20
|
+
|
21
|
+
def memory
|
22
|
+
memory = %x|grep MemTotal /proc/meminfo|
|
23
|
+
memory = memory.to_s.match(/(\d+)/)
|
24
|
+
(memory[1].to_i * 0.000001).to_i
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/common/log.rb
CHANGED
@@ -14,6 +14,8 @@ module Liri
|
|
14
14
|
FOLDER_PATH = File.join(Dir.pwd, "/#{FOLDER_NAME}")
|
15
15
|
FILE_NAME = 'liri.log'
|
16
16
|
|
17
|
+
attr_reader :folder_path
|
18
|
+
|
17
19
|
def initialize(shift_age, folder_path:, file_name:, stdout: true)
|
18
20
|
@stdout = stdout
|
19
21
|
@shift_age = shift_age
|
@@ -27,37 +29,37 @@ module Liri
|
|
27
29
|
end
|
28
30
|
|
29
31
|
def debug(text, stdout = false)
|
30
|
-
puts(text) if stdout
|
32
|
+
puts(ColorizeText.debug(text)) if stdout
|
31
33
|
@stdout_logger.debug(text) if @stdout
|
32
34
|
@file_logger.debug(text)
|
33
35
|
end
|
34
36
|
|
35
37
|
def info(text, stdout = false)
|
36
|
-
puts(text) if stdout
|
38
|
+
puts(ColorizeText.default(text)) if stdout
|
37
39
|
@stdout_logger.info(text) if @stdout
|
38
40
|
@file_logger.info(text)
|
39
41
|
end
|
40
42
|
|
41
43
|
def warn(text, stdout = false)
|
42
|
-
puts(text) if stdout
|
44
|
+
puts(ColorizeText.warn(text)) if stdout
|
43
45
|
@stdout_logger.warn(text) if @stdout
|
44
46
|
@file_logger.warn(text)
|
45
47
|
end
|
46
48
|
|
47
49
|
def error(text, stdout = false)
|
48
|
-
puts(text) if stdout
|
50
|
+
puts(ColorizeText.error(text)) if stdout
|
49
51
|
@stdout_logger.error(text) if @stdout
|
50
52
|
@file_logger.error(text)
|
51
53
|
end
|
52
54
|
|
53
55
|
def fatal(text, stdout = false)
|
54
|
-
puts(text) if stdout
|
56
|
+
puts(ColorizeText.fatal(text)) if stdout
|
55
57
|
@stdout_logger.fatal(text) if @stdout
|
56
58
|
@file_logger.fatal(text)
|
57
59
|
end
|
58
60
|
|
59
61
|
def unknown(text, stdout = false)
|
60
|
-
puts(text) if stdout
|
62
|
+
puts(ColorizeText.unknown(text)) if stdout
|
61
63
|
@stdout_logger.unknown(text) if @stdout
|
62
64
|
@file_logger.unknown(text)
|
63
65
|
end
|
@@ -65,12 +67,12 @@ module Liri
|
|
65
67
|
private
|
66
68
|
def create_stdout_logger
|
67
69
|
@stdout_logger = Logger.new(STDOUT, @shift_age)
|
68
|
-
@stdout_logger.formatter = Liri::Common::LogFormatter.colorize(Liri.setup.log.stdout.colorize)
|
70
|
+
@stdout_logger.formatter = Liri::Common::LogFormatter.colorize(Liri.setup.general.log.stdout.colorize)
|
69
71
|
end
|
70
72
|
|
71
73
|
def create_file_logger
|
72
74
|
@file_logger = Logger.new(@file_path, @shift_age)
|
73
|
-
@file_logger.formatter = Liri::Common::LogFormatter.colorize(Liri.setup.log.file.colorize)
|
75
|
+
@file_logger.formatter = Liri::Common::LogFormatter.colorize(Liri.setup.general.log.file.colorize)
|
74
76
|
end
|
75
77
|
|
76
78
|
def create_log_folder
|
@@ -78,18 +80,58 @@ module Liri
|
|
78
80
|
end
|
79
81
|
end
|
80
82
|
|
83
|
+
class ColorizeText
|
84
|
+
COLORS = {
|
85
|
+
DEBUG: '0;36', # cyan
|
86
|
+
ERROR: '0;31', # red
|
87
|
+
INFO: '0;32', # green
|
88
|
+
WARN: '0;33', # orange
|
89
|
+
FATAL: '0;35', # pink
|
90
|
+
ANY: '0;36', # cyan
|
91
|
+
DEFAULT: '1;0' # white
|
92
|
+
}
|
93
|
+
|
94
|
+
class << self
|
95
|
+
def default(text)
|
96
|
+
colorize(text, :DEFAULT)
|
97
|
+
end
|
98
|
+
|
99
|
+
def debug(text)
|
100
|
+
colorize(text, :DEBUG)
|
101
|
+
end
|
102
|
+
|
103
|
+
def info(text)
|
104
|
+
colorize(text, :INFO)
|
105
|
+
end
|
106
|
+
|
107
|
+
def warn(text)
|
108
|
+
colorize(text, :WARN)
|
109
|
+
end
|
110
|
+
|
111
|
+
def error(text)
|
112
|
+
colorize(text, :ERROR)
|
113
|
+
end
|
114
|
+
|
115
|
+
def fatal(text)
|
116
|
+
colorize(text, :FATAL)
|
117
|
+
end
|
118
|
+
|
119
|
+
def unknown(text)
|
120
|
+
colorize(text, :ANY)
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
def colorize(text, color)
|
126
|
+
"\e[#{COLORS[color]}m#{text}\e[0m"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
81
131
|
class LogFormatter
|
82
132
|
DATETIME_FORMAT = "%d-%m-%Y %H:%M"
|
83
133
|
|
84
|
-
SEVERITY_COLORS =
|
85
|
-
DEBUG: '0;36', # cyan
|
86
|
-
ERROR: '0;31', # red
|
87
|
-
INFO: '0;32', # green
|
88
|
-
WARN: '0;33', # orange
|
89
|
-
FATAL: '0;35', # pink
|
90
|
-
ANY: '0;36', # cyan
|
91
|
-
DEFAULT: '1;0' # white
|
92
|
-
}
|
134
|
+
SEVERITY_COLORS = ColorizeText::COLORS
|
93
135
|
|
94
136
|
class << self
|
95
137
|
def colorize(type)
|
data/lib/common/manager_data.rb
CHANGED
@@ -8,10 +8,10 @@ module Liri
|
|
8
8
|
module Common
|
9
9
|
# Esta clase guarda los datos del Manager
|
10
10
|
class ManagerData
|
11
|
-
attr_accessor :
|
11
|
+
attr_accessor :tests_results_folder_path, :compressed_file_path, :user, :password
|
12
12
|
|
13
|
-
def initialize(
|
14
|
-
@
|
13
|
+
def initialize(tests_results_folder_path:, compressed_file_path:, user:, password:)
|
14
|
+
@tests_results_folder_path = tests_results_folder_path
|
15
15
|
@compressed_file_path = compressed_file_path
|
16
16
|
@user = user
|
17
17
|
@password = password
|
@@ -19,7 +19,7 @@ module Liri
|
|
19
19
|
|
20
20
|
def to_h
|
21
21
|
{
|
22
|
-
|
22
|
+
tests_results_folder_path: @tests_results_folder_path,
|
23
23
|
compressed_file_path: @compressed_file_path,
|
24
24
|
user: @user,
|
25
25
|
password: @password
|
data/lib/common/progressbar.rb
CHANGED
@@ -12,16 +12,16 @@ module Liri
|
|
12
12
|
module Progressbar
|
13
13
|
class << self
|
14
14
|
def start(params = {})
|
15
|
-
@
|
15
|
+
@progressing = true
|
16
16
|
progressbar = ProgressBar.create(params)
|
17
17
|
Thread.new do
|
18
|
-
while @
|
18
|
+
while @progressing
|
19
19
|
progressbar.increment
|
20
20
|
sleep(0.1)
|
21
21
|
end
|
22
22
|
end
|
23
23
|
yield
|
24
|
-
@
|
24
|
+
@progressing = false
|
25
25
|
end
|
26
26
|
end
|
27
27
|
end
|
data/lib/common/setup.rb
CHANGED
@@ -14,21 +14,31 @@ module Liri
|
|
14
14
|
MANAGER_FOLDER_NAME = 'manager'
|
15
15
|
AGENT_FOLDER_NAME = 'agent'
|
16
16
|
|
17
|
-
attr_reader :setup_folder_path, :setup_file_path, :logs_folder_path, :manager_folder_path,
|
17
|
+
attr_reader :setup_folder_path, :setup_file_path, :logs_folder_path, :manager_folder_path,
|
18
|
+
:manager_tests_results_folder_path, :agent_folder_path
|
18
19
|
|
19
|
-
def initialize(destination_folder_path)
|
20
|
+
def initialize(destination_folder_path, program, manager_tests_results_folder_time: nil)
|
20
21
|
@setup_folder_path = File.join(destination_folder_path, '/', SETUP_FOLDER_NAME)
|
21
22
|
@setup_file_path = File.join(@setup_folder_path, '/', SETUP_FILE_NAME)
|
22
23
|
@logs_folder_path = File.join(@setup_folder_path, '/', LOGS_FOLDER_NAME)
|
23
24
|
@manager_folder_path = File.join(@setup_folder_path, '/', MANAGER_FOLDER_NAME)
|
25
|
+
@manager_tests_results_folder_path = File.join(@manager_folder_path, '/', "#{manager_tests_results_folder_time}_tests_results") if manager_tests_results_folder_time
|
24
26
|
@agent_folder_path = File.join(@setup_folder_path, '/', AGENT_FOLDER_NAME)
|
27
|
+
@program = program
|
25
28
|
end
|
26
29
|
|
27
30
|
def init
|
28
31
|
create_folder(@setup_folder_path)
|
29
32
|
create_folder(@logs_folder_path)
|
30
|
-
|
31
|
-
|
33
|
+
|
34
|
+
case @program
|
35
|
+
when :manager
|
36
|
+
create_folder(@manager_folder_path)
|
37
|
+
create_folder(@manager_tests_results_folder_path) if @manager_tests_results_folder_path
|
38
|
+
when :agent
|
39
|
+
create_folder(@agent_folder_path)
|
40
|
+
end
|
41
|
+
|
32
42
|
create_setup_file
|
33
43
|
end
|
34
44
|
|
data/lib/common/source_code.rb
CHANGED
@@ -8,7 +8,7 @@ module Liri
|
|
8
8
|
DECOMPRESSED_FOLDER_NAME = 'decompressed'
|
9
9
|
attr_reader :folder_path, :compressed_file_folder_path, :compressed_file_path, :decompressed_file_folder_path
|
10
10
|
|
11
|
-
def initialize(folder_path, compressed_file_folder_path, compression_class, unit_test_class)
|
11
|
+
def initialize(folder_path, compressed_file_folder_path, ignored_folders, compression_class, unit_test_class)
|
12
12
|
@folder_path = folder_path
|
13
13
|
@folder_name = @folder_path.split('/').last
|
14
14
|
@compressed_file_folder_path = compressed_file_folder_path
|
@@ -17,7 +17,7 @@ module Liri
|
|
17
17
|
# Inicializa un compresor acorde a compression_class, la siguiente línea en realidad hace lo siguiente:
|
18
18
|
# @compressor = Liri::Common::Compressor::Zip.new(input_dir, output_file)
|
19
19
|
# compression_class en este caso es Zip pero podría ser otro si existiera la implementación, por ejemplo Rar
|
20
|
-
@compressor = Object.const_get(compression_class).new(@folder_path, @compressed_file_path)
|
20
|
+
@compressor = Object.const_get(compression_class).new(@folder_path, @compressed_file_path, ignored_folders)
|
21
21
|
# Inicializa un ejecutor de pruebas acorde a unit_test_class, la siguiente línea en realidad hace lo siguiente:
|
22
22
|
# @unit_test = Liri::Common::UnitTest::Rspec.new(source_code_folder_path)
|
23
23
|
# unit_test_class en este caso es Rspec pero podría ser otro si existiera la implementación, por ejemplo UnitTest
|
data/lib/common/tests_result.rb
CHANGED
@@ -8,12 +8,19 @@ module Liri
|
|
8
8
|
module Common
|
9
9
|
# Esta clase se encarga de guardar y procesar el archivo de resultados
|
10
10
|
class TestsResult
|
11
|
+
attr_reader :examples, :failures, :pending, :passed
|
12
|
+
|
11
13
|
def initialize(folder_path)
|
12
14
|
@folder_path = folder_path
|
13
|
-
@
|
14
|
-
@
|
15
|
-
@
|
16
|
-
@
|
15
|
+
@examples = 0
|
16
|
+
@failures = 0
|
17
|
+
@pending = 0
|
18
|
+
@passed = 0
|
19
|
+
@finish_in = 0
|
20
|
+
@files_load = 0
|
21
|
+
@failures_list = ''
|
22
|
+
@failed_examples = ''
|
23
|
+
@failed_files = ''
|
17
24
|
end
|
18
25
|
|
19
26
|
def save(file_name, raw_tests_result)
|
@@ -22,28 +29,36 @@ module Liri
|
|
22
29
|
file_path
|
23
30
|
end
|
24
31
|
|
25
|
-
def build_file_name(agent_ip_address,
|
26
|
-
"batch_#{
|
32
|
+
def build_file_name(agent_ip_address, batch_num)
|
33
|
+
"batch_#{batch_num}_agent_#{agent_ip_address}_tests_results"
|
27
34
|
end
|
28
35
|
|
29
36
|
# Procesa el resultado crudo de las pruebas unitarias y lo devuelve en formato hash manejable
|
30
37
|
# Ejemplo del hash retornado:
|
31
|
-
# {
|
38
|
+
# { examples: 0, failures: 0, pending: 0, passed: 0, finish_in: 0, files_load: 0,
|
39
|
+
# failures_list: '', failed_examples: '' }
|
32
40
|
def process(tests_result_file_name)
|
33
41
|
file_path = File.join(@folder_path, '/', tests_result_file_name)
|
42
|
+
# A veces no se encuentra el archivo de resultados, la siguiente condicional es para evitar errores relativos a esto
|
43
|
+
return {} unless File.exist?(file_path)
|
44
|
+
|
34
45
|
result_hash = process_tests_result_file(file_path)
|
35
46
|
update_partial_result(result_hash)
|
36
|
-
#print_partial_result(result_hash)
|
37
47
|
result_hash
|
38
48
|
end
|
39
49
|
|
40
50
|
def print_summary
|
41
|
-
|
51
|
+
Liri.logger.info("\n#{@examples} examples, #{@passed} passed, #{@failures} failures\n", true)
|
52
|
+
end
|
53
|
+
|
54
|
+
def print_detailed_failures
|
55
|
+
Liri.logger.info("\nFailures: ", true) unless @failures_list.empty?
|
56
|
+
Liri.logger.info(@failures_list, true)
|
42
57
|
end
|
43
58
|
|
44
|
-
def
|
45
|
-
|
46
|
-
|
59
|
+
def print_summary_failures
|
60
|
+
Liri.logger.info("\nFailed examples: ", true) unless @failed_examples.empty?
|
61
|
+
Liri.logger.info(@failed_examples, true)
|
47
62
|
end
|
48
63
|
|
49
64
|
private
|
@@ -51,10 +66,12 @@ module Liri
|
|
51
66
|
# Recibe el resultado crudo de las pruebas unitarias
|
52
67
|
# Procesa el archivo con los resultados crudos y lo devuelve en formato hash manejable
|
53
68
|
# Ejemplo del hash retornado:
|
54
|
-
# {result: '.F', failures: '',
|
69
|
+
# {result: '.F', failures: '', examples: 2, failures: 1, failed_examples: ''}
|
55
70
|
def process_tests_result_file(file_path)
|
56
|
-
result_hash = {failures:
|
71
|
+
result_hash = { examples: 0, failures: 0, pending: 0, passed: 0, finish_in: 0, files_load: 0,
|
72
|
+
failures_list: '', failed_examples: '', failed_files: '' }
|
57
73
|
flag = ''
|
74
|
+
@failures_lists_count = @failures
|
58
75
|
File.foreach(file_path) do |line|
|
59
76
|
if flag == '' && line.strip.start_with?('Randomized')
|
60
77
|
flag = 'Randomized'
|
@@ -67,6 +84,9 @@ module Liri
|
|
67
84
|
end
|
68
85
|
|
69
86
|
if ['Randomized', 'Failures', ''].include?(flag) && line.strip.start_with?('Finished')
|
87
|
+
values = finish_in_values(line)
|
88
|
+
result_hash[:finish_in] = values[:finish_in]
|
89
|
+
result_hash[:files_load] = values[:files_load]
|
70
90
|
flag = 'Finished'
|
71
91
|
next
|
72
92
|
end
|
@@ -78,15 +98,20 @@ module Liri
|
|
78
98
|
|
79
99
|
case flag
|
80
100
|
when 'Failures'
|
81
|
-
|
101
|
+
line = fix_failure_number(line)
|
102
|
+
result_hash[:failures_list] << line
|
82
103
|
when 'Finished'
|
83
|
-
values = line
|
84
|
-
result_hash[:
|
85
|
-
result_hash[:
|
86
|
-
result_hash[:
|
104
|
+
values = finished_summary_values(line)
|
105
|
+
result_hash[:examples] = values[:examples]
|
106
|
+
result_hash[:failures] = values[:failures]
|
107
|
+
result_hash[:passed] = result_hash[:examples] - result_hash[:failures]
|
108
|
+
result_hash[:pending] = values[:pending]
|
87
109
|
flag = ''
|
88
110
|
when 'Failed'
|
89
|
-
|
111
|
+
if line.strip.start_with?('rspec')
|
112
|
+
result_hash[:failed_examples] << line
|
113
|
+
result_hash[:failed_files] << "#{failed_example(line)}\n"
|
114
|
+
end
|
90
115
|
end
|
91
116
|
end
|
92
117
|
|
@@ -94,22 +119,38 @@ module Liri
|
|
94
119
|
end
|
95
120
|
|
96
121
|
def update_partial_result(hash_result)
|
97
|
-
@
|
98
|
-
@
|
99
|
-
@
|
100
|
-
@
|
122
|
+
@examples += hash_result[:examples]
|
123
|
+
@failures += hash_result[:failures]
|
124
|
+
@pending += hash_result[:pending]
|
125
|
+
@passed += hash_result[:passed]
|
126
|
+
@failures_list << hash_result[:failures_list]
|
127
|
+
@failed_examples << hash_result[:failed_examples]
|
128
|
+
@failed_files << hash_result[:failed_files]
|
101
129
|
end
|
102
130
|
|
103
|
-
def
|
104
|
-
|
105
|
-
|
106
|
-
end
|
131
|
+
def finish_in_values(line)
|
132
|
+
UnitTest::RspecResultParser.finish_in_values(line)
|
133
|
+
end
|
107
134
|
|
108
|
-
|
109
|
-
|
110
|
-
|
135
|
+
def finished_summary_values(line)
|
136
|
+
UnitTest::RspecResultParser.finished_summary_values(line)
|
137
|
+
end
|
138
|
+
|
139
|
+
def failed_example(line)
|
140
|
+
# get string like this "/spec/failed_spec.rb:4"
|
141
|
+
failed_example = UnitTest::RspecResultParser.failed_example(line)
|
142
|
+
# return "failed_spec.rb:4"
|
143
|
+
failed_example.split("/").last
|
111
144
|
end
|
112
145
|
|
146
|
+
def fix_failure_number(line)
|
147
|
+
line_number_regex = /(\d+\))/
|
148
|
+
if line.strip.start_with?(line_number_regex)
|
149
|
+
@failures_lists_count += 1
|
150
|
+
line.gsub!(line_number_regex, "#{@failures_lists_count})")
|
151
|
+
end
|
152
|
+
line
|
153
|
+
end
|
113
154
|
end
|
114
155
|
end
|
115
156
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# = text_time_parser.rb
|
2
|
+
#
|
3
|
+
# @author Rodrigo Fernández
|
4
|
+
#
|
5
|
+
# == Clase TextTimeParser
|
6
|
+
|
7
|
+
require 'bigdecimal'
|
8
|
+
|
9
|
+
module Liri
|
10
|
+
module Common
|
11
|
+
# Esta clase parsea texto en horas, minutos y segundos a un valor decimal en segundos
|
12
|
+
class TextTimeParser
|
13
|
+
class << self
|
14
|
+
def to_seconds(text_time)
|
15
|
+
values = text_time.split(' ')
|
16
|
+
case values.size
|
17
|
+
when 2 # cuando se tiene por ejemplo '15 minutes'
|
18
|
+
text_time_to_seconds(values[0], values[1])
|
19
|
+
when 4 # cuando se tiene por ejemplo '1 minute 5 seconds'
|
20
|
+
text_time_to_seconds(values[0], values[1]) + text_time_to_seconds(values[2], values[3])
|
21
|
+
when 6 # cuando se tiene por ejemplo '1 hour 30 minutes 25 seconds'
|
22
|
+
text_time_to_seconds(values[0], values[1]) +
|
23
|
+
text_time_to_seconds(values[2], values[3]) +
|
24
|
+
text_time_to_seconds(values[4], values[5])
|
25
|
+
end
|
26
|
+
# queda pendiente agregar el caso de dias, horas, minutos y segundos, además hace falta verificar como lo muestra Rspec
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def text_time_to_seconds(number, text)
|
32
|
+
# Se usa BigDecimal porque
|
33
|
+
# En una multiplicación normal: (203.033*3600).to_f = 730918.7999999999
|
34
|
+
# Con BigDecimal: (BigDecimal('203.033') * 3600).to_f = 730918.8
|
35
|
+
time = BigDecimal(number)
|
36
|
+
time_in_seconds = case text
|
37
|
+
when 'second', 'seconds' then time
|
38
|
+
when 'minute', 'minutes' then time * 60
|
39
|
+
when 'hour', 'hours' then time * 3600
|
40
|
+
when 'day', 'days' then time * 86_400
|
41
|
+
end
|
42
|
+
|
43
|
+
# time in seconds puede contener valores BigDecimal como este 0.744e2 que al hacerle to_f devuelve 74.4
|
44
|
+
# Aún así conviene mantener el valor en BigDecimal para las operaciones matematicas y solo cuando se va a mostrar
|
45
|
+
# los datos en pantalla se debe hacer el to_f
|
46
|
+
time_in_seconds
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# = tty_progressbar.rb
|
2
|
+
#
|
3
|
+
# @author Rodrigo Fernández
|
4
|
+
#
|
5
|
+
# == Módulo TtyProgressbar
|
6
|
+
require "tty-progressbar"
|
7
|
+
|
8
|
+
module Liri
|
9
|
+
module Common
|
10
|
+
# Este módulo se encarga de mostrar una barra de progreso
|
11
|
+
module TtyProgressbar
|
12
|
+
ANIMATION = [
|
13
|
+
"■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□",
|
14
|
+
"□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■",
|
15
|
+
"□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□",
|
16
|
+
"□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□",
|
17
|
+
]
|
18
|
+
|
19
|
+
ANIMATION2 = [
|
20
|
+
"■□□□■□□□■□□□■□□□■□□",
|
21
|
+
"□■□□□■□□□■□□□■□□□■□",
|
22
|
+
"□□■□□□■□□□■□□□■□□□■",
|
23
|
+
"□□□■□□□■□□□■□□□■□□□",
|
24
|
+
]
|
25
|
+
class << self
|
26
|
+
# Example:
|
27
|
+
# Common::TtyProgressbar.start("Compressing source code |:bar| Time: :elapsed", total: nil, width: 80) do
|
28
|
+
# ...code
|
29
|
+
# end
|
30
|
+
def start(format, params = {})
|
31
|
+
params[:unknown] = ANIMATION[0]
|
32
|
+
progressbar = TTY::ProgressBar.new(format, params)
|
33
|
+
# Es importante iniciar la barra porque TimeFormatter.call accede a su start_time y si no se inició la barra
|
34
|
+
# entonces ocurre un error
|
35
|
+
progressbar.start
|
36
|
+
progressbar.use(Common::TtyProgressbar::TimeFormatter)
|
37
|
+
|
38
|
+
Thread.new do
|
39
|
+
animation_count = 0
|
40
|
+
while !progressbar.stopped?
|
41
|
+
progressbar.advance
|
42
|
+
|
43
|
+
progressbar.update(unknown: ANIMATION[animation_count])
|
44
|
+
animation_count += 1
|
45
|
+
animation_count = 0 if animation_count == 3
|
46
|
+
|
47
|
+
sleep(0.1)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
yield
|
51
|
+
progressbar.update(total: 1) # Esto hace que la barra cambie a al estilo completado con un porcentaje del 100%
|
52
|
+
progressbar.stop
|
53
|
+
rescue TypeError
|
54
|
+
# Se captura la excepción solo para evitar un error en start_time mas abajo
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# From
|
59
|
+
class TimeFormatter
|
60
|
+
include TTY::ProgressBar::Formatter[/:time/i]
|
61
|
+
|
62
|
+
def call(value) # specify how display string is formatted
|
63
|
+
# access current progress bar instance to read start time
|
64
|
+
elapsed = Duration.humanize(Time.now - progress.start_time, times_round: Liri.times_round, times_round_type: Liri.times_round_type)
|
65
|
+
value.gsub(matcher, elapsed) # replace :time token with a value
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|