liri 0.1.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.
@@ -0,0 +1,258 @@
1
+ =begin
2
+ Este modulo es el punto de entrada del programa principal
3
+ =end
4
+ require 'all_libraries'
5
+
6
+ module Liri
7
+ class Manager
8
+ UDP_REQUEST_DELAY = 3
9
+ attr_reader :agents
10
+
11
+ class << self
12
+ # Inicia la ejecución del Manager
13
+ # @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)
15
+ 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
+
22
+ user, password = get_credentials
23
+
24
+ source_code = compress_source_code
25
+
26
+ manager_data = manager_data(user, password, source_code)
27
+
28
+ all_tests = get_all_tests(source_code)
29
+
30
+ test_result = Liri::Manager::TestResult.new
31
+
32
+ manager = Manager.new(Liri.udp_port, Liri.tcp_port, all_tests, test_result)
33
+
34
+ threads = []
35
+ 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
37
+
38
+ Liri.init_exit(stop, threads, 'Manager')
39
+ Liri.logger.info("Proceso Manager terminado")
40
+ rescue SignalException => e
41
+ Liri.logger.info("Exception(#{e}) Proceso Manager terminado manualmente")
42
+ Liri.kill(threads)
43
+ end
44
+
45
+ def test_samples_by_runner
46
+ Liri.setup.test_samples_by_runner
47
+ end
48
+
49
+ private
50
+ def valid_project
51
+ if File.exist?(File.join(Dir.pwd, 'Gemfile'))
52
+ true
53
+ else
54
+ Liri.logger.info("No se encuentra un archivo Gemfile por lo que se asume que el directorio actual no corresponde a un proyecto Ruby")
55
+ Liri.logger.info("Liri sólo puede ejecutarse en proyectos Ruby")
56
+ Liri.logger.info("Proceso Manager terminado")
57
+ false
58
+ end
59
+ end
60
+
61
+ def compressed_file_folder_path
62
+ File.join(Liri::Common.setup_folder_path, '/manager')
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)
71
+ credential.get
72
+ end
73
+
74
+ def compress_source_code
75
+ source_code = Liri::Common::SourceCode.new(Liri::MANAGER_FOLDER_PATH, Liri.compression_class, Liri.unit_test_class)
76
+ Liri::Common::Benchmarking.start(start_msg: "Comprimiendo código fuente. Espere... ") do
77
+ source_code.compress_folder
78
+ end
79
+ puts ''
80
+ source_code
81
+ end
82
+
83
+ def manager_data(user, password, source_code)
84
+ # puts "User: #{user} Password: #{password}"
85
+ [user, password, source_code.compressed_file_path].join(';')
86
+ end
87
+
88
+ def get_all_tests(source_code)
89
+ all_tests = {}
90
+ Liri::Common::Benchmarking.start(start_msg: "Obteniendo conjunto de pruebas. Espere... ") do
91
+ all_tests = source_code.all_tests
92
+ end
93
+ puts ''
94
+ all_tests
95
+ end
96
+ end
97
+
98
+ def initialize(udp_port, tcp_port, all_tests, test_result)
99
+ @udp_port = udp_port
100
+ @udp_socket = UDPSocket.new
101
+ @tcp_port = tcp_port
102
+
103
+ @all_tests = all_tests
104
+ @all_tests_count = all_tests.size
105
+ @all_tests_results = {}
106
+ @all_tests_results_count = 0
107
+ @all_tests_processing_count = 0
108
+ @agents = {}
109
+
110
+ @agents_search_processing_enabled = true
111
+ @test_processing_enabled = true
112
+
113
+ @test_result = test_result
114
+ @semaphore = Mutex.new
115
+ end
116
+
117
+ # 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(user_data)
119
+ # El cliente udp se ejecuta en bucle dentro de un hilo, esto permite realizar otras tareas mientras este hilo sigue sondeando
120
+ # la red para obtener mas Agents. Una vez que los tests terminan de ejecutarse, este hilo será finalizado.
121
+ Thread.new do
122
+ puts "\nBuscando Agentes... Espere"
123
+ Liri.logger.info("Se emite un broadcast cada #{UDP_REQUEST_DELAY} segundos en el puerto UDP: #{@udp_port}
124
+ (Se mantiene escaneando la red para encontrar Agents)
125
+ ")
126
+ while @agents_search_processing_enabled
127
+ @udp_socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_BROADCAST, true)
128
+ @udp_socket.send(user_data, 0, '<broadcast>', @udp_port)
129
+ sleep(UDP_REQUEST_DELAY) # Se pausa un momento antes de efectuar nuevamente la petición broadcast
130
+ end
131
+ end
132
+ end
133
+
134
+ # Inicia un servidor tcp para procesar los pruebas después de haberse iniciado la conexión a través de udp
135
+ def start_server_socket_to_process_tests(search_agents_thread)
136
+ begin
137
+ tcp_socket = TCPServer.new(@tcp_port) # se hace un bind al puerto dado
138
+ rescue Errno::EADDRINUSE => e
139
+ Liri.logger.error("Exception(#{e}) Puerto TCP #{@tcp_port} ocupado.")
140
+ Thread.kill(search_agents_thread)
141
+ Thread.exit
142
+ end
143
+
144
+ Liri.logger.info("En espera para establecer conexión con los Agents en el puerto TCP: #{@tcp_port}
145
+ (Se espera que algún Agent se conecte para ejecutar las pruebas como respuesta al broadcast UDP)
146
+ ")
147
+ # El siguiente bucle permite que varios clientes es decir Agents se conecten
148
+ # De: http://www.w3big.com/es/ruby/ruby-socket-programming.html
149
+ while @test_processing_enabled
150
+ Thread.start(tcp_socket.accept) do |client|
151
+ agent_ip_address = client.remote_address.ip_address
152
+ response = client.recvfrom(1000).first
153
+
154
+ if @all_tests.empty?
155
+ # No importa lo que le haga, el broadcast udp no se muere al instante y el agente sigue respondiendo
156
+ # Las siguientes dos lineas son para que se deje de hacer el broadcast pero aun asi se llegan a hacer
157
+ # 3 a 4 broadcast antes de que se finalize el proceso, al parecer el broadcast va a tener que quedar asi
158
+ # y mejorar el codigo para que se envien test pendientes para eso hay que llevar una lista de test pendientes
159
+ # tests enviados sin resultados, tests finalizados, si se recibe respuesta al broadcast se trata de enviar primero test pendientes
160
+ # luego test enviados sin resultados o sino ignorar
161
+ Thread.kill(search_agents_thread)
162
+ @agents_search_processing_enabled = false
163
+ Liri.logger.info("Se termina cualquier proceso pendiente con el Agent #{agent_ip_address}")
164
+ Liri.logger.info(response)
165
+ client.close
166
+ Thread.exit
167
+ end
168
+
169
+ puts "\nConexión iniciada con el Agente: #{agent_ip_address}"
170
+ Liri.logger.info("Respuesta al broadcast recibida del Agent: #{agent_ip_address} en el puerto TCP: #{@tcp_port}
171
+ => Agent #{agent_ip_address}: #{response}
172
+ ")
173
+
174
+ while @all_tests.any?
175
+ tests = samples
176
+ break if tests.empty?
177
+ begin
178
+ client.puts(tests.to_json) # Este envía un hash bastante grande al cliente y siempre llega, pero la respuesta del cliente a veces no llega todo, porque?
179
+ response = client.recvfrom(1000).first
180
+ rescue Errno::EPIPE => e
181
+ # Esto al parecer se da cuando el Agent ya cerró las conexiones y el Manager intenta contactar
182
+ Liri.logger.error("Exception(#{e}) El Agent #{agent_ip_address} ya terminó la conexión")
183
+ # Si el Agente ya no responde es mejor romper el bucle para que no quede colgado
184
+ break
185
+ end
186
+ # TODO A veces se tiene un error de parseo JSON, de ser asi los resultados no pueden procesarse,
187
+ # hay que arreglar esto, mientras, se captura el error para que no falle
188
+ begin
189
+ tests_result = response
190
+ Liri.logger.debug("Resultados de la ejecución de las pruebas recibidas del Agent #{agent_ip_address}:")
191
+ Liri.logger.debug("RAW:")
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)
197
+ rescue JSON::ParserError => e
198
+ Liri.logger.error("Exception(#{e}) Error de parseo JSON")
199
+ end
200
+ end
201
+
202
+ update_processing_statuses
203
+ puts ''
204
+ Liri.logger.info("Se termina la conexión con el Agent #{agent_ip_address}")
205
+ begin
206
+ client.puts('exit') # Se envía el string exit para que el Agent sepa que el proceso terminó
207
+ client.close # se desconecta el cliente
208
+ rescue Errno::EPIPE => e
209
+ # Esto al parecer se da cuando el Agent ya cerró las conexiones y el Manager intenta contactar
210
+ Liri.logger.error("Exception(#{e}) El Agent #{agent_ip_address} ya terminó la conexión")
211
+ # Si el Agente ya no responde es mejor terminar el hilo. Aunque igual quedará colgado el Manager
212
+ # mientras sigan pruebas pendientes
213
+ Thread.exit
214
+ end
215
+ end
216
+ end
217
+
218
+ Liri.clean_folder(Liri::MANAGER_FOLDER_PATH)
219
+ @test_result.print_summary
220
+ end
221
+
222
+ def update_processing_statuses
223
+ @semaphore.synchronize {
224
+ @test_processing_enabled = false if @all_tests_count == @all_tests_results_count
225
+ @agents_search_processing_enabled = false if @all_tests_count == @all_tests_processing_count
226
+ }
227
+ end
228
+
229
+ def update_all_tests_results_count(new_count)
230
+ @all_tests_results_count += new_count
231
+ end
232
+
233
+ def update_all_tests_processing_count(new_count)
234
+ @all_tests_processing_count += new_count
235
+ end
236
+
237
+ def samples
238
+ _samples = nil
239
+ # Varios hilos no deben acceder simultaneamente al siguiente bloque porque actualiza variables compartidas
240
+ @semaphore.synchronize {
241
+ _samples = @all_tests.sample!(Manager.test_samples_by_runner)
242
+ puts ''
243
+ Liri.logger.info("Cantidad de pruebas pendientes: #{@all_tests.size}")
244
+ update_all_tests_processing_count(_samples.size)
245
+ }
246
+ _samples
247
+ end
248
+
249
+ def process_tests_result(tests, tests_result)
250
+ # Varios hilos no deben acceder simultaneamente al siguiente bloque porque actualiza variables compartidas
251
+ @semaphore.synchronize {
252
+ update_all_tests_results_count(tests.size)
253
+ @test_result.print_process(tests_result)
254
+ @test_result.update(tests_result)
255
+ }
256
+ end
257
+ end
258
+ end
@@ -0,0 +1,70 @@
1
+ =begin
2
+ Este modulo se encarga de manejar la configuración
3
+ =end
4
+ require 'yaml'
5
+ require 'json'
6
+
7
+ module Liri
8
+ class Manager
9
+ class Setup
10
+ FILE_NAME = 'liri-config.yml'
11
+ TEMPLATE_PATH = File.join(File.dirname(File.dirname(File.dirname(__FILE__))), 'template/liri-config.yml')
12
+
13
+ def initialize(folder_path)
14
+ # Crea la carpeta en donde se guardarán los datos relativos a liri, ya sean archivos comprimidos,
15
+ # archivos descomprimidos, configuraciones, etc.
16
+ Dir.mkdir(folder_path) unless Dir.exist?(folder_path)
17
+
18
+ @file_path = File.join(folder_path, '/', FILE_NAME)
19
+ end
20
+
21
+ # Crea un archivo de configuración en la raiz del proyecto desde un template
22
+ def create
23
+ File.open(@file_path, "w") do |output_file|
24
+ File.foreach(TEMPLATE_PATH) do |input_line|
25
+ output_file.write(input_line)
26
+ end
27
+ end
28
+ true
29
+ end
30
+
31
+ # Retorna los datos del archivo de configuración
32
+ def load
33
+ if File.exist?(@file_path)
34
+ data = YAML.load(File.read(@file_path))
35
+ JSON.parse(data.to_json, object_class: OpenStruct)
36
+ else
37
+ raise Liri::FileNotFoundError.new(@file_path)
38
+ end
39
+ end
40
+
41
+ # Borra el archivo de configuración
42
+ def delete
43
+ if File.exist?(@file_path)
44
+ File.delete(@file_path)
45
+ true
46
+ else
47
+ false
48
+ end
49
+ end
50
+
51
+ def set(value, *keys)
52
+ data = YAML.load(File.read(@file_path))
53
+ keys = keys.first
54
+ aux = data
55
+ keys.each_with_index do |key, index|
56
+ if (keys[index + 1])
57
+ aux = data[key]
58
+ else
59
+ aux[key] = value
60
+ end
61
+ end
62
+ File.open(@file_path, 'w') { |f| f.write data.to_yaml }
63
+ end
64
+
65
+ def path
66
+ @file_path
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,38 @@
1
+ # Procesa el resultado de las pruebas unitarias
2
+ module Liri
3
+ class Manager
4
+ class TestResult
5
+ def initialize
6
+ @example_quantity = 0
7
+ @failure_quantity = 0
8
+ @passed_quantity = 0
9
+ end
10
+ def update(test_result)
11
+ @example_quantity += test_result['example_quantity']
12
+ @failure_quantity += test_result['failure_quantity']
13
+ @passed_quantity += (@example_quantity - @failure_quantity)
14
+ end
15
+
16
+ def print_process(test_result)
17
+ passed_quantity = test_result['example_quantity'] - test_result['failure_quantity']
18
+ passed_quantity.times do
19
+ print '.'
20
+ end
21
+
22
+ test_result['failure_quantity'].times do
23
+ print 'F'
24
+ end
25
+ end
26
+
27
+ def print_summary
28
+ print_examples_and_failures
29
+ end
30
+
31
+ private
32
+
33
+ def print_examples_and_failures
34
+ puts "#{@example_quantity} examples, #{@failure_quantity} failures\n\n"
35
+ end
36
+ end
37
+ end
38
+ end
data/lib/task.rb ADDED
@@ -0,0 +1,15 @@
1
+ =begin
2
+ Este módulo ejecuta tareas de apoyo
3
+ =end
4
+
5
+ module Liri
6
+ module Task
7
+ class << self
8
+ def tests_count
9
+ source_code = Liri::Common::SourceCode.new('', Liri.compression_class, Liri.unit_test_class)
10
+ source_code.all_tests.size
11
+ end
12
+ end
13
+ end
14
+ end
15
+
data/liri.gemspec ADDED
@@ -0,0 +1,58 @@
1
+ require_relative 'lib/liri.rb'
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = Liri::NAME
5
+ spec.version = Liri::VERSION
6
+ spec.authors = ["Rodrigo Fernández", "Leslie López"]
7
+ spec.email = ["rofaccess@gmail.com", "leslyee.05@gmail.com"]
8
+
9
+ spec.summary = "TFG Project"
10
+ spec.description = "Test distributor executor"
11
+ spec.homepage = "https://github.com/rofaccess/tfg"
12
+ spec.license = "MIT"
13
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
14
+
15
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
16
+
17
+ spec.metadata["homepage_uri"] = spec.homepage
18
+ spec.metadata["source_code_uri"] = "https://github.com/rofaccess/tfg/liri"
19
+ spec.metadata["changelog_uri"] = "https://github.com/rofaccess/tfg/blob/master/liri/README.md"
20
+
21
+ # Specify which files should be added to the gem when it is released.
22
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
24
+ # La siguiente línea debe comentarse al momento de ejecutar liri m dentro del proyecto Liri porque al tratar de ejecutar pruebas de este código fuente
25
+ # con agentes ejecutandose en distribuciones Linux y Ubuntu, por algún motivo ocurre el siguiente error:
26
+ #
27
+ # Esta línea es crítica, debe estar si o sí habilitada al compilar la gema
28
+ # Esta línea define que carpetas se excluyen del .gem generado al compilar la gema
29
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|documents|installers)/}) }
30
+ end
31
+ spec.bindir = "exe"
32
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
33
+ spec.require_paths = ["lib"]
34
+
35
+ # Rubyzip is a ruby library for reading and writing zip files. https://github.com/rubyzip/rubyzip
36
+ # Se usa para comprimir el código fuente en el Manager y descomprimir el código fuente recibido en el Agent
37
+ spec.add_runtime_dependency('rubyzip', '~> 2')
38
+
39
+ # The complete solution for Ruby command-line executables. https://github.com/commander-rb/commander
40
+ # Se usa para pasar comandos a la gema Liri
41
+ spec.add_runtime_dependency('commander', '~> 4')
42
+
43
+ # HighLine was designed to ease the tedious tasks of doing console input and output with low-level methods like gets and puts. https://github.com/JEG2/highline
44
+ # Se usa para pedir password en el Manager
45
+ spec.add_runtime_dependency('highline', '~> 2')
46
+
47
+ # Net::SCP is a pure-Ruby implementation of the SCP protocol. This operates over SSH (and requires the Net::SSH library), and allows files and directory trees to be copied to and from a remote server. https://github.com/net-ssh/net-scp
48
+ # Se usa para obtener el código fuente del Manager desde el Agent
49
+ spec.add_runtime_dependency('net-scp', '~> 3')
50
+
51
+ # A Ruby gem for converting seconds into human-readable format. https://github.com/digaev/to_duration
52
+ # Se usa para convertir un tiempo en segundos a un formato más legible
53
+ spec.add_runtime_dependency('to_duration', '~> 1')
54
+
55
+ # Ruby internationalization and localization (i18n) solution. https://github.com/ruby-i18n/i18n
56
+ # Este es un requerimiento de la gema to_duration
57
+ spec.add_runtime_dependency('i18n', '~> 1')
58
+ end
@@ -0,0 +1,28 @@
1
+ # Este archivo .yml define ciertas configuraciones para la ejecución del sistema
2
+ # Configuración del Nombre del archivo de código fuente comprimido enviado a los agentes
3
+ # Obs.: Puede que no sea útil para el usuario poder cambiar este nombre
4
+ compressed_file_name: compressed_source_code
5
+ # Configuración de la cantidad de tests a ejecutar por tandas
6
+ test_samples_by_runner: 100
7
+ log:
8
+ stdout:
9
+ # Configuración que define si se muestra el log de ejecución del programa en línea de comando. Puede ser true o false.
10
+ # Por defecto siempre se imprimirá el log en un archivo dentro de la carpeta logs
11
+ show: true
12
+ # Define los colores del texto del log. Puede ser none (no muestra colores), severity (colorea código de error),
13
+ # severity_date (colorea código de error y fecha), full (colorea el texto entero).
14
+ colorize: full
15
+ file:
16
+ colorize: full
17
+ library:
18
+ # Configuración de la librería de compresión a utilizar para comprimir el código fuente
19
+ # Valores soportados: Sólo Zip hasta ahora
20
+ compression: Zip
21
+ # Configuración de la librería de pruebas unitarias a ejecutar
22
+ # Valores soportados: Sólo Rspec hasta ahora
23
+ unit_test: Rspec
24
+ ports:
25
+ # Configuración del puerto a través del cual se realizará la primera comunicación entre Manager y Agent
26
+ udp: 2000
27
+ # Configuración del puerto a través del cual el Agent y el Manager intercambiarán las pruebas a ejecutar y los resultados de esa ejecución
28
+ tcp: 2500
metadata ADDED
@@ -0,0 +1,182 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: liri
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Rodrigo Fernández
8
+ - Leslie López
9
+ autorequire:
10
+ bindir: exe
11
+ cert_chain: []
12
+ date: 2022-05-08 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rubyzip
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '2'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '2'
28
+ - !ruby/object:Gem::Dependency
29
+ name: commander
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '4'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '4'
42
+ - !ruby/object:Gem::Dependency
43
+ name: highline
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '2'
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '2'
56
+ - !ruby/object:Gem::Dependency
57
+ name: net-scp
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '3'
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '3'
70
+ - !ruby/object:Gem::Dependency
71
+ name: to_duration
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: '1'
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '1'
84
+ - !ruby/object:Gem::Dependency
85
+ name: i18n
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - "~>"
89
+ - !ruby/object:Gem::Version
90
+ version: '1'
91
+ type: :runtime
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - "~>"
96
+ - !ruby/object:Gem::Version
97
+ version: '1'
98
+ description: Test distributor executor
99
+ email:
100
+ - rofaccess@gmail.com
101
+ - leslyee.05@gmail.com
102
+ executables:
103
+ - liri
104
+ extensions: []
105
+ extra_rdoc_files: []
106
+ files:
107
+ - ".gitignore"
108
+ - ".rakeTasks"
109
+ - ".rspec"
110
+ - ".rubocop.yml"
111
+ - ".ruby-gemset"
112
+ - ".ruby-version"
113
+ - ".travis.yml"
114
+ - CODE_OF_CONDUCT.md
115
+ - Gemfile
116
+ - Gemfile.lock
117
+ - LICENSE.txt
118
+ - README.md
119
+ - Rakefile
120
+ - bin/bundle
121
+ - bin/coderay
122
+ - bin/commander
123
+ - bin/htmldiff
124
+ - bin/ldiff
125
+ - bin/liri
126
+ - bin/rake
127
+ - bin/rspec
128
+ - bin/rubocop
129
+ - bin/ruby-parse
130
+ - bin/ruby-rewrite
131
+ - bin/yard
132
+ - bin/yardoc
133
+ - bin/yri
134
+ - config/locales/to_duration_es.yml
135
+ - exe/liri
136
+ - lib/agent/agent.rb
137
+ - lib/agent/runner.rb
138
+ - lib/all_libraries.rb
139
+ - lib/common/benchmarking.rb
140
+ - lib/common/compressor/zip.rb
141
+ - lib/common/log.rb
142
+ - lib/common/source_code.rb
143
+ - lib/common/unit_test/rspec.rb
144
+ - lib/exe/agent.rb
145
+ - lib/exe/manager.rb
146
+ - lib/hash_extend.rb
147
+ - lib/liri.rb
148
+ - lib/manager/credential.rb
149
+ - lib/manager/manager.rb
150
+ - lib/manager/setup.rb
151
+ - lib/manager/test_result.rb
152
+ - lib/task.rb
153
+ - liri.gemspec
154
+ - template/liri-config.yml
155
+ homepage: https://github.com/rofaccess/tfg
156
+ licenses:
157
+ - MIT
158
+ metadata:
159
+ allowed_push_host: https://rubygems.org
160
+ homepage_uri: https://github.com/rofaccess/tfg
161
+ source_code_uri: https://github.com/rofaccess/tfg/liri
162
+ changelog_uri: https://github.com/rofaccess/tfg/blob/master/liri/README.md
163
+ post_install_message:
164
+ rdoc_options: []
165
+ require_paths:
166
+ - lib
167
+ required_ruby_version: !ruby/object:Gem::Requirement
168
+ requirements:
169
+ - - ">="
170
+ - !ruby/object:Gem::Version
171
+ version: 2.3.0
172
+ required_rubygems_version: !ruby/object:Gem::Requirement
173
+ requirements:
174
+ - - ">="
175
+ - !ruby/object:Gem::Version
176
+ version: '0'
177
+ requirements: []
178
+ rubygems_version: 3.1.4
179
+ signing_key:
180
+ specification_version: 4
181
+ summary: TFG Project
182
+ test_files: []