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/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
|