teuton 2.5.0 → 2.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/docs/CHANGELOG.md +2 -0
- data/docs/changelog/todo.md +4 -6
- data/docs/changelog/v2.6.md +4 -0
- data/docs/changelog/v2.7.md +11 -0
- data/docs/diagram.md +55 -0
- data/docs/learn/02-target.md +37 -12
- data/docs/learn/03-remote_hosts.md +1 -1
- data/docs/learn/04-config.md +1 -1
- data/docs/learn/06-cmd_check.md +4 -6
- data/docs/learn/07-target_weight.md +1 -3
- data/docs/learn/21-exit_codes.md +9 -16
- data/docs/learn/README.md +1 -1
- data/lib/teuton/case/case.rb +95 -0
- data/lib/teuton/{case_manager/case → case}/close.rb +2 -0
- data/lib/teuton/{case_manager/case → case}/config.rb +3 -3
- data/lib/teuton/{case_manager/case → case/deprecated}/runner.rb +14 -10
- data/lib/teuton/case/deprecated/utils.rb +40 -0
- data/lib/teuton/{case_manager/case/dsl/main.rb → case/dsl/all.rb} +0 -2
- data/lib/teuton/{case_manager/case → case}/dsl/expect.rb +25 -9
- data/lib/teuton/{case_manager/case → case}/dsl/goto.rb +10 -9
- data/lib/teuton/{case_manager/case → case}/dsl/log.rb +1 -2
- data/lib/teuton/{case_manager/case → case}/dsl/macro.rb +2 -2
- data/lib/teuton/{case_manager/case → case}/dsl/send.rb +3 -8
- data/lib/teuton/case/execute/execute_base.rb +55 -0
- data/lib/teuton/case/execute/execute_local.rb +29 -0
- data/lib/teuton/case/execute/execute_manager.rb +56 -0
- data/lib/teuton/case/execute/execute_ssh.rb +90 -0
- data/lib/teuton/case/execute/execute_telnet.rb +56 -0
- data/lib/teuton/{case_manager/case → case}/play.rb +11 -14
- data/lib/teuton/case_manager/case_manager.rb +20 -39
- data/lib/teuton/case_manager/check_cases.rb +14 -12
- data/lib/teuton/case_manager/dsl.rb +7 -9
- data/lib/teuton/case_manager/export_manager.rb +19 -6
- data/lib/teuton/case_manager/hall_of_fame.rb +9 -10
- data/lib/teuton/case_manager/report.rb +11 -9
- data/lib/teuton/case_manager/send_manager.rb +17 -0
- data/lib/teuton/{report/show.rb → case_manager/show_report.rb} +4 -6
- data/lib/teuton/case_manager/utils.rb +2 -43
- data/lib/teuton/check/dsl.rb +1 -2
- data/lib/teuton/check/laboratory.rb +7 -7
- data/lib/teuton/check/show.rb +5 -8
- data/lib/teuton/cli.rb +10 -0
- data/lib/teuton/readme/dsl.rb +5 -7
- data/lib/teuton/readme/lang.rb +3 -2
- data/lib/teuton/readme/readme.rb +15 -18
- data/lib/teuton/report/formatter/default/array.rb +6 -5
- data/lib/teuton/report/formatter/default/txt.rb +1 -0
- data/lib/teuton/report/formatter/resume/array.rb +3 -3
- data/lib/teuton/report/formatter/resume/html.rb +2 -2
- data/lib/teuton/report/report.rb +6 -5
- data/lib/teuton/skeleton.rb +8 -10
- data/lib/teuton/{application.rb → utils/application.rb} +13 -5
- data/lib/teuton/utils/name_file_finder.rb +40 -45
- data/lib/teuton/utils/project.rb +73 -0
- data/lib/teuton/{case_manager/case → utils}/result/ext_array.rb +5 -5
- data/lib/teuton/{case_manager/case → utils}/result/result.rb +10 -8
- data/lib/teuton/utils/settings.rb +12 -0
- data/lib/teuton/utils/verbose.rb +2 -2
- data/lib/teuton/version.rb +1 -1
- data/lib/teuton.rb +28 -27
- metadata +43 -30
- data/lib/teuton/case_manager/case/case.rb +0 -117
- data/lib/teuton/case_manager/case/main.rb +0 -7
- data/lib/teuton/case_manager/main.rb +0 -3
- /data/lib/teuton/{case_manager/case → case}/builtin/main.rb +0 -0
- /data/lib/teuton/{case_manager/case → case}/builtin/package.rb +0 -0
- /data/lib/teuton/{case_manager/case → case}/builtin/service.rb +0 -0
- /data/lib/teuton/{case_manager/case → case}/builtin/teuton_file.rb +0 -0
- /data/lib/teuton/{case_manager/case → case}/builtin/teuton_host.rb +0 -0
- /data/lib/teuton/{case_manager/case → case}/builtin/user.rb +0 -0
- /data/lib/teuton/{case_manager/case → case}/dsl/getset.rb +0 -0
- /data/lib/teuton/{case_manager/case → case}/dsl/target.rb +0 -0
- /data/lib/teuton/{case_manager/case → case}/dsl/unique.rb +0 -0
- /data/lib/teuton/{case_manager/case → utils}/result/ext_compare.rb +0 -0
- /data/lib/teuton/{case_manager/case → utils}/result/ext_filter.rb +0 -0
@@ -1,13 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "../
|
3
|
+
require_relative "../execute/execute_manager"
|
4
4
|
|
5
|
-
# Case class -> DSL module:
|
6
|
-
# * goto
|
7
|
-
# * run
|
8
5
|
module DSL
|
9
6
|
##
|
10
|
-
#
|
7
|
+
# DSL run and goto
|
8
|
+
# run: It's the same as goto :localhost
|
11
9
|
# @param command (String)
|
12
10
|
# @param args (Hash)
|
13
11
|
def run(command, args = {})
|
@@ -17,7 +15,7 @@ module DSL
|
|
17
15
|
goto(host, args)
|
18
16
|
end
|
19
17
|
|
20
|
-
# Run command from the host identify as
|
18
|
+
# Run command from the host identify as "host"
|
21
19
|
# goto :host1, :execute => "command"
|
22
20
|
def goto(host = :localhost, args = {})
|
23
21
|
@result.reset
|
@@ -27,9 +25,12 @@ module DSL
|
|
27
25
|
tempfile(args[:tempfile]) if args[:tempfile]
|
28
26
|
@action[:encoding] = args[:encoding] || "UTF-8"
|
29
27
|
|
30
|
-
|
31
|
-
|
32
|
-
|
28
|
+
ExecuteManager.new(self).call(host)
|
29
|
+
@action[:output] = if @result.content.size < 2
|
30
|
+
@result.value.clone
|
31
|
+
else
|
32
|
+
"(#{@result.content.size} lines)"
|
33
|
+
end
|
33
34
|
end
|
34
35
|
alias_method :on, :goto
|
35
36
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "
|
3
|
+
require_relative "../../utils/project"
|
4
4
|
|
5
5
|
# DSL module methods: assert, missing_method
|
6
6
|
module DSL
|
@@ -9,7 +9,7 @@ module DSL
|
|
9
9
|
# @param name (String) Macro name
|
10
10
|
# @param input (Hash) Macro params
|
11
11
|
def macro(name, input = {})
|
12
|
-
macros =
|
12
|
+
macros = Project.value[:macros]
|
13
13
|
unless macros[name]
|
14
14
|
log("Macro #{name} not found!", :error)
|
15
15
|
return
|
@@ -1,12 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Case->DSL#
|
4
|
-
# * send
|
5
|
-
# * tempfile
|
6
|
-
# * tempdir
|
7
|
-
# * remote_tempdir
|
8
|
-
# * remote_tempfile
|
9
3
|
module DSL
|
4
|
+
# * send, tempfile, tempdir, remote_tempdir, remote_tempfile
|
10
5
|
def send(args = {})
|
11
6
|
return if skip?
|
12
7
|
|
@@ -52,7 +47,7 @@ module DSL
|
|
52
47
|
name = "teuton.tmp" if input == :default
|
53
48
|
|
54
49
|
@action[:tempfile] = File.join(@tmpdir, name)
|
55
|
-
@action[:remote_tempfile] = File.join(
|
50
|
+
@action[:remote_tempfile] = File.join(remote_tempdir, name)
|
56
51
|
|
57
52
|
@action[:tempfile]
|
58
53
|
end
|
@@ -66,6 +61,6 @@ module DSL
|
|
66
61
|
end
|
67
62
|
|
68
63
|
def remote_tempdir
|
69
|
-
|
64
|
+
File.join("/", "tmp") # TODO: Remove this?
|
70
65
|
end
|
71
66
|
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require_relative "../../utils/verbose"
|
2
|
+
|
3
|
+
class ExecuteBase
|
4
|
+
include Verbose
|
5
|
+
|
6
|
+
def initialize(parent)
|
7
|
+
@parent = parent
|
8
|
+
# READ: @config, cmd = action[:command]
|
9
|
+
# WRITE: @action, @result, @session
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def config
|
15
|
+
@parent.config
|
16
|
+
end
|
17
|
+
|
18
|
+
def action
|
19
|
+
@parent.action
|
20
|
+
end
|
21
|
+
|
22
|
+
def result
|
23
|
+
@parent.result
|
24
|
+
end
|
25
|
+
|
26
|
+
def sessions
|
27
|
+
@parent.sessions
|
28
|
+
end
|
29
|
+
|
30
|
+
def log(...)
|
31
|
+
@parent.log(...)
|
32
|
+
end
|
33
|
+
|
34
|
+
def conn_status
|
35
|
+
@parent.conn_status
|
36
|
+
end
|
37
|
+
|
38
|
+
def encode_and_split(encoding, text)
|
39
|
+
# Convert text to UTF-8 deleting unknown chars
|
40
|
+
text ||= "" # Ensure text is not nil
|
41
|
+
flag = [:default, "UTF-8"].include? encoding
|
42
|
+
return text.encode("UTF-8", invalid: :replace).split("\n") if flag
|
43
|
+
|
44
|
+
# Convert text from input ENCODING to UTF-8
|
45
|
+
ec = Encoding::Converter.new(encoding.to_s, "UTF-8")
|
46
|
+
begin
|
47
|
+
text = ec.convert(text)
|
48
|
+
rescue => e
|
49
|
+
puts "[ERROR] #{e}: Declare text encoding..."
|
50
|
+
puts " run 'command', on: :host, :encoding => 'ISO-8859-1'"
|
51
|
+
end
|
52
|
+
|
53
|
+
text.split("\n")
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "open3"
|
2
|
+
require "rainbow"
|
3
|
+
require_relative "../../utils/project"
|
4
|
+
require_relative "../../utils/verbose"
|
5
|
+
require_relative "execute_base"
|
6
|
+
|
7
|
+
class ExecuteLocal < ExecuteBase
|
8
|
+
def call
|
9
|
+
action[:conn_type] = :local
|
10
|
+
response = my_execute(action[:command], action[:encoding])
|
11
|
+
result.exitcode = response[:exitcode]
|
12
|
+
result.content = response[:content]
|
13
|
+
end
|
14
|
+
|
15
|
+
def my_execute(cmd, encoding = "UTF-8")
|
16
|
+
return {exitcode: 0, content: ""} if Project.debug?
|
17
|
+
|
18
|
+
begin
|
19
|
+
text, status = Open3.capture2e(cmd)
|
20
|
+
exitcode = status.exitstatus
|
21
|
+
rescue => e
|
22
|
+
verbose Rainbow("!").green
|
23
|
+
text = e.to_s
|
24
|
+
exitcode = 1
|
25
|
+
end
|
26
|
+
content = encode_and_split(encoding, text)
|
27
|
+
{exitcode: exitcode, content: content}
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require_relative "execute_local"
|
2
|
+
require_relative "execute_ssh"
|
3
|
+
require_relative "execute_telnet"
|
4
|
+
|
5
|
+
class ExecuteManager
|
6
|
+
def initialize(parent)
|
7
|
+
@parent = parent
|
8
|
+
# READ: @config, cmd = action[:command]
|
9
|
+
# WRITE: @action, @result, @session
|
10
|
+
# my_execute, encode_and_split methods?
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(host)
|
14
|
+
start_time = Time.now
|
15
|
+
run_on(host)
|
16
|
+
action[:duration] = (Time.now - start_time).round(3)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def action
|
22
|
+
@parent.action
|
23
|
+
end
|
24
|
+
|
25
|
+
def config
|
26
|
+
@parent.config
|
27
|
+
end
|
28
|
+
|
29
|
+
def log
|
30
|
+
@parent.log
|
31
|
+
end
|
32
|
+
|
33
|
+
def run_on(host)
|
34
|
+
protocol = config.get("#{host}_protocol".to_sym)
|
35
|
+
ip = config.get("#{host}_ip".to_sym)
|
36
|
+
|
37
|
+
if protocol.to_s.downcase == "local" || host.to_s == "localhost"
|
38
|
+
# Protocol force => local
|
39
|
+
ExecuteLocal.new(@parent).call
|
40
|
+
elsif protocol.to_s.downcase == "ssh"
|
41
|
+
# Protocol force => ssh
|
42
|
+
ExecuteSSH.new(@parent).call(host)
|
43
|
+
elsif protocol.to_s.downcase == "telnet"
|
44
|
+
# Protocol force => telnet
|
45
|
+
ExecuteTelnet.new(@parent).call(host)
|
46
|
+
elsif ip.to_s.downcase == "localhost" || ip.to_s.include?("127.0.0.")
|
47
|
+
# run_cmd_localhost
|
48
|
+
ExecuteLocal.new(@parent).call
|
49
|
+
elsif ip == "NODATA"
|
50
|
+
log("#{host} IP not found!", :error)
|
51
|
+
else
|
52
|
+
# run_cmd_remote_ssh host
|
53
|
+
ExecuteSSH.new(@parent).call(host)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require "net/ssh"
|
2
|
+
require "net/sftp"
|
3
|
+
require "rainbow"
|
4
|
+
require_relative "../../utils/project"
|
5
|
+
require_relative "../../utils/verbose"
|
6
|
+
require_relative "execute_base"
|
7
|
+
|
8
|
+
class ExecuteSSH < ExecuteBase
|
9
|
+
def call(input_hostname)
|
10
|
+
action[:conn_type] = :ssh
|
11
|
+
hostname = input_hostname.to_s
|
12
|
+
ip = config.get("#{hostname}_ip".to_sym).to_s
|
13
|
+
username = config.get("#{hostname}_username".to_sym).to_s
|
14
|
+
password = config.get("#{hostname}_password".to_sym).to_s
|
15
|
+
port = config.get("#{hostname}_port".to_sym).to_i
|
16
|
+
port = 22 if port.zero?
|
17
|
+
|
18
|
+
unless config.get("#{hostname}_route".to_sym) == "NODATA"
|
19
|
+
# Reconfigure command with gateway. Example host1_route: IP.
|
20
|
+
# hostname2 = hostname ¿not used?
|
21
|
+
ip2 = ip
|
22
|
+
username2 = username
|
23
|
+
password2 = password
|
24
|
+
command2 = action[:command]
|
25
|
+
hostname = config.get("#{hostname}_route".to_sym)
|
26
|
+
ip = config.get("#{hostname}_ip".to_sym).to_s
|
27
|
+
username = config.get("#{hostname}_username".to_sym).to_s
|
28
|
+
password = config.get("#{hostname}_password".to_sym).to_s
|
29
|
+
ostype = config.get("#{hostname}_ostype".to_sym).to_s
|
30
|
+
|
31
|
+
action[:command] = if ostype.downcase.start_with? "win"
|
32
|
+
"echo y | plink #{username2}@#{ip2} -ssh -pw #{password2} \"#{command2}\""
|
33
|
+
else
|
34
|
+
"sshpass -p #{password2} #{username2}@#{ip2} #{command2}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
text = ""
|
39
|
+
exitcode = 0
|
40
|
+
begin
|
41
|
+
if sessions[hostname].nil?
|
42
|
+
sessions[hostname] = Net::SSH.start(
|
43
|
+
ip,
|
44
|
+
username,
|
45
|
+
port: port,
|
46
|
+
password: password,
|
47
|
+
keepalive: true,
|
48
|
+
timeout: 30,
|
49
|
+
non_interactive: true
|
50
|
+
)
|
51
|
+
end
|
52
|
+
text = if sessions[hostname].instance_of? Net::SSH::Connection::Session
|
53
|
+
sessions[hostname].exec!(action[:command])
|
54
|
+
else
|
55
|
+
"SSH: NO CONNECTION!"
|
56
|
+
end
|
57
|
+
exitcode = text.exitstatus
|
58
|
+
rescue Errno::EHOSTUNREACH
|
59
|
+
sessions[hostname] = :nosession
|
60
|
+
conn_status[hostname] = :host_unreachable
|
61
|
+
exitcode = -1
|
62
|
+
log("Host #{ip} unreachable!", :error)
|
63
|
+
rescue Net::SSH::AuthenticationFailed
|
64
|
+
sessions[hostname] = :nosession
|
65
|
+
conn_status[hostname] = :error_authentication_failed
|
66
|
+
exitcode = -1
|
67
|
+
log("SSH::AuthenticationFailed!", :error)
|
68
|
+
rescue Net::SSH::HostKeyMismatch
|
69
|
+
sessions[hostname] = :nosession
|
70
|
+
conn_status[hostname] = :host_key_mismatch
|
71
|
+
exitcode = -1
|
72
|
+
log("SSH::HostKeyMismatch!", :error)
|
73
|
+
log("* The destination server's fingerprint is not matching " \
|
74
|
+
"what is in your local known_hosts file.", :error)
|
75
|
+
log("* Remove the existing entry in your local known_hosts file", :error)
|
76
|
+
log("* Try this => ssh-keygen -f '/home/USERNAME/.ssh/known_hosts' " \
|
77
|
+
"-R #{ip}", :error)
|
78
|
+
rescue => e
|
79
|
+
sessions[hostname] = :nosession
|
80
|
+
conn_status[hostname] = :error
|
81
|
+
exitcode = -1
|
82
|
+
log("[#{e.class}] SSH on <#{username}@#{ip}>" \
|
83
|
+
" exec: #{action[:command]}", :error)
|
84
|
+
end
|
85
|
+
output = encode_and_split(action[:encoding], text)
|
86
|
+
result.exitcode = exitcode
|
87
|
+
result.content = output
|
88
|
+
result.content.compact!
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require "net/telnet"
|
2
|
+
require "rainbow"
|
3
|
+
require_relative "../../utils/project"
|
4
|
+
require_relative "../../utils/verbose"
|
5
|
+
require_relative "execute_base"
|
6
|
+
|
7
|
+
class ExecuteTelnet < ExecuteBase
|
8
|
+
def call(input_hostname)
|
9
|
+
action[:conn_type] = :telnet
|
10
|
+
hostname = input_hostname.to_s
|
11
|
+
ip = config.get((hostname + "_ip").to_sym)
|
12
|
+
username = config.get((hostname + "_username").to_sym).to_s
|
13
|
+
password = config.get((hostname + "_password").to_sym).to_s
|
14
|
+
text = ""
|
15
|
+
begin
|
16
|
+
if sessions[hostname].nil? || sessions[hostname] == :ok
|
17
|
+
h = Net::Telnet.new(
|
18
|
+
"Host" => ip,
|
19
|
+
"Timeout" => 30,
|
20
|
+
"Prompt" => /login|teuton|[$%#>]/
|
21
|
+
)
|
22
|
+
# "Prompt" => Regexp.new(username[1, 40]))
|
23
|
+
# "Prompt" => /[$%#>] \z/n)
|
24
|
+
h.login(username, password)
|
25
|
+
h.cmd(action[:command]) { |i| text << i }
|
26
|
+
h.close
|
27
|
+
sessions[hostname] = :ok
|
28
|
+
else
|
29
|
+
text = "TELNET: NO CONNECTION!"
|
30
|
+
end
|
31
|
+
rescue Net::OpenTimeout
|
32
|
+
sessions[hostname] = :nosession
|
33
|
+
conn_status[hostname] = :open_timeout
|
34
|
+
verbose Rainbow(Application.instance.letter[:error]).red.bright
|
35
|
+
log(" ExceptionType=<Net::OpenTimeout> doing <telnet #{ip}>", :error)
|
36
|
+
log(" └── Revise host IP!", :warn)
|
37
|
+
rescue Net::ReadTimeout
|
38
|
+
sessions[hostname] = :nosession
|
39
|
+
conn_status[hostname] = :read_timeout
|
40
|
+
verbose Rainbow(Application.instance.letter[:error]).red.bright
|
41
|
+
log(" ExceptionType=<Net::ReadTimeout> doing <telnet #{ip}>", :error)
|
42
|
+
rescue => e
|
43
|
+
sessions[hostname] = :nosession
|
44
|
+
conn_status[hostname] = :error
|
45
|
+
verbose Rainbow(Application.instance.letter[:error]).red.bright
|
46
|
+
log(" ExceptionType=<#{e.class}> doing telnet on <#{username}@#{ip}>" \
|
47
|
+
" exec: #{action[:command]}", :error)
|
48
|
+
log(" └── username=<#{username}>, password=<#{password}>," \
|
49
|
+
" ip=<#{ip}>, HOSTID=<#{hostname}>", :warn)
|
50
|
+
end
|
51
|
+
output = encode_and_split(action[:encoding], text)
|
52
|
+
result.exitcode = -1
|
53
|
+
result.content = output
|
54
|
+
result.content.compact!
|
55
|
+
end
|
56
|
+
end
|
@@ -1,27 +1,22 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Case class:
|
4
|
-
# * play
|
5
|
-
# * play_in_parallel
|
6
|
-
# * play_in_sequence
|
7
|
-
# * fill_report
|
8
|
-
# * close_opened_sessions
|
9
3
|
class Case
|
4
|
+
# Case class: play
|
5
|
+
# TODO: Encapsulate code into PlayManager class
|
6
|
+
# * play_in_parallel, play_in_sequence, fill_report, close_opened_sessions
|
7
|
+
# READ: sessions, config, groups, action, report
|
8
|
+
# TODO: groups from Project or from Case???
|
10
9
|
def play
|
11
10
|
if skip?
|
12
11
|
verbose Rainbow("S").green
|
13
12
|
return false
|
14
13
|
end
|
15
14
|
start_time = Time.now
|
16
|
-
|
17
|
-
play_in_sequence
|
18
|
-
else
|
19
|
-
play_in_parallel
|
20
|
-
end
|
15
|
+
play_groups_in_sequence
|
21
16
|
fill_report(start_time, Time.now)
|
22
17
|
close_opened_sessions
|
23
18
|
end
|
24
|
-
|
19
|
+
alias_method :start, :play
|
25
20
|
|
26
21
|
def close_opened_sessions
|
27
22
|
@sessions.each_value do |s|
|
@@ -31,14 +26,16 @@ class Case
|
|
31
26
|
|
32
27
|
private
|
33
28
|
|
34
|
-
def
|
29
|
+
def play_groups_in_sequence
|
30
|
+
verboseln "\n=> Starting case [#{@config.get(:tt_members)}]" if get(:tt_sequence) == true
|
35
31
|
@groups.each do |t|
|
36
32
|
@action[:groupname] = t[:name]
|
33
|
+
# TODO: @parent(case).instance_eval(&t[:block])
|
37
34
|
instance_eval(&t[:block])
|
38
35
|
end
|
39
36
|
end
|
40
37
|
|
41
|
-
def
|
38
|
+
def play_groups_in_parallel
|
42
39
|
verboseln "Starting case [#{@config.get(:tt_members)}]"
|
43
40
|
@groups.each do |t|
|
44
41
|
verbose "* Processing [#{t[:name]}] "
|
@@ -1,15 +1,15 @@
|
|
1
|
-
|
2
|
-
require "singleton"
|
3
|
-
require_relative "../application"
|
1
|
+
require_relative "../case/case"
|
4
2
|
require_relative "../report/report"
|
5
|
-
require_relative "../
|
3
|
+
require_relative "../utils/project"
|
4
|
+
require_relative "../utils/verbose"
|
6
5
|
require_relative "../utils/configfile_reader"
|
7
|
-
require_relative "case/case"
|
8
6
|
require_relative "export_manager"
|
9
|
-
require_relative "
|
7
|
+
require_relative "send_manager"
|
8
|
+
require_relative "show_report"
|
9
|
+
require_relative "check_cases"
|
10
|
+
require_relative "report"
|
11
|
+
require_relative "utils"
|
10
12
|
|
11
|
-
# This class does all the job
|
12
|
-
# Organize the hole job, sending orders to others classes
|
13
13
|
# * initialize
|
14
14
|
# * play
|
15
15
|
# Split into several files:
|
@@ -18,8 +18,8 @@ require_relative "main"
|
|
18
18
|
# * case_manager/hall_of_fame
|
19
19
|
# * case_manager/report
|
20
20
|
class CaseManager
|
21
|
-
include Singleton
|
22
21
|
include Utils
|
22
|
+
include Verbose
|
23
23
|
attr_reader :report, :cases
|
24
24
|
|
25
25
|
def initialize
|
@@ -28,51 +28,32 @@ class CaseManager
|
|
28
28
|
@report.filename = "resume"
|
29
29
|
end
|
30
30
|
|
31
|
-
##
|
32
|
-
# Execute "play" order: Start every single case test
|
33
|
-
# @param block (Block)
|
34
31
|
def play(&block)
|
32
|
+
# Execute "play" order: Start every single case test
|
35
33
|
check_cases!
|
36
34
|
instance_eval(&block)
|
37
35
|
# Run export if user pass option command "--export=json"
|
38
|
-
i =
|
36
|
+
i = Project.value[:options]["export"]
|
39
37
|
export(format: i.to_sym) unless i.nil?
|
40
38
|
# Accept "configfile" param REVISE There exists?
|
41
|
-
i =
|
39
|
+
i = Project.value[:options]["configfile"]
|
42
40
|
export(format: i.to_sym) unless i.nil?
|
43
41
|
end
|
44
42
|
|
45
|
-
##
|
46
|
-
# Execute "export" order: Export every case report
|
47
|
-
# @param args (Hash) Export options
|
48
43
|
def export(args = {})
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
exit 1
|
56
|
-
end
|
57
|
-
ExportManager.run(@report, @cases, args)
|
44
|
+
ExportManager.new.call(
|
45
|
+
@report,
|
46
|
+
@cases,
|
47
|
+
args,
|
48
|
+
Project.value[:format]
|
49
|
+
)
|
58
50
|
end
|
59
51
|
|
60
|
-
##
|
61
|
-
# Execute "send" order: Send every case report
|
62
|
-
# @param args (Hash) Send options
|
63
52
|
def send(args = {})
|
64
|
-
|
65
|
-
puts ""
|
66
|
-
puts Rainbow("-" * 50).green
|
67
|
-
puts Rainbow("Sending files...#{args}").color(:green)
|
68
|
-
@cases.each { |c| threads << Thread.new { c.send(args) } }
|
69
|
-
threads.each(&:join)
|
70
|
-
puts Rainbow("Sending finished!").color(:green)
|
71
|
-
puts Rainbow("-" * 50).green
|
53
|
+
SendManager.new.call(@cases, args)
|
72
54
|
end
|
73
55
|
|
74
56
|
def show(options = {verbose: 1})
|
75
|
-
|
76
|
-
ShowReport.new(@report).call(verbose)
|
57
|
+
ShowReport.new(@report).call(options[:verbose])
|
77
58
|
end
|
78
59
|
end
|
@@ -1,32 +1,33 @@
|
|
1
1
|
require_relative "hall_of_fame"
|
2
|
+
require_relative "../utils/project"
|
2
3
|
|
3
4
|
class CaseManager
|
4
5
|
private
|
5
6
|
|
6
|
-
##
|
7
|
-
# Start checking every single case
|
8
7
|
def check_cases!
|
9
|
-
|
10
|
-
|
8
|
+
# Start checking every single case
|
9
|
+
app = Project.value
|
11
10
|
# Load configurations from config file
|
12
|
-
configdata = ConfigFileReader.read(
|
13
|
-
app
|
14
|
-
app
|
15
|
-
app
|
16
|
-
app
|
11
|
+
configdata = ConfigFileReader.read(Project.value[:config_path])
|
12
|
+
app[:ialias] = configdata[:alias]
|
13
|
+
app[:global] = configdata[:global]
|
14
|
+
app[:global][:tt_testname] = app[:global][:tt_testname] || app[:test_name]
|
15
|
+
app[:global][:tt_sequence] = false if app[:global][:tt_sequence].nil?
|
17
16
|
|
18
17
|
# Create out dir
|
19
|
-
outdir = app
|
18
|
+
outdir = app[:global][:tt_outdir] || File.join("var", app[:global][:tt_testname])
|
20
19
|
ensure_dir outdir
|
21
20
|
@report.output_dir = outdir
|
22
21
|
|
23
22
|
# Fill report head
|
24
|
-
open_main_report(app
|
23
|
+
open_main_report(app[:config_path])
|
25
24
|
|
26
25
|
# create cases and run
|
27
26
|
configdata[:cases].each { |config| @cases << Case.new(config) }
|
28
27
|
start_time = run_all_cases # run cases
|
29
28
|
|
29
|
+
# TODO: merge these 2 methdos
|
30
|
+
# TODO: CloseManager.call ???
|
30
31
|
uniques = collect_uniques_for_all_cases
|
31
32
|
close_reports_for_all_cases(uniques)
|
32
33
|
close_main_report(start_time)
|
@@ -36,7 +37,8 @@ class CaseManager
|
|
36
37
|
start_time = Time.now
|
37
38
|
verboseln Rainbow("-" * 36).green
|
38
39
|
verboseln Rainbow("Started at #{start_time}").green
|
39
|
-
if Application.instance.global[:tt_sequence] == true
|
40
|
+
# if Application.instance.global[:tt_sequence] == true
|
41
|
+
if Project.value[:global][:tt_sequence] == true
|
40
42
|
# Run every case in sequence
|
41
43
|
@cases.each(&:play)
|
42
44
|
else
|
@@ -1,16 +1,15 @@
|
|
1
|
-
require_relative "../
|
1
|
+
require_relative "../utils/project"
|
2
2
|
require_relative "case_manager"
|
3
3
|
|
4
4
|
def use(filename)
|
5
5
|
filename += ".rb"
|
6
|
-
|
7
|
-
rbfiles = File.join(app.project_path, "**", filename)
|
6
|
+
rbfiles = File.join(Project.value[:project_path], "**", filename)
|
8
7
|
files = Dir.glob(rbfiles)
|
9
8
|
findfiles = []
|
10
9
|
files.sort.each { |f| findfiles << f if f.include?(filename) }
|
11
10
|
begin
|
12
11
|
require_relative findfiles.first
|
13
|
-
|
12
|
+
Project.value[:uses] << File.basename(findfiles.first)
|
14
13
|
rescue
|
15
14
|
puts "[ERROR] Unknown file : #{filename}"
|
16
15
|
puts " Check line : use '#{filename}'"
|
@@ -19,7 +18,7 @@ def use(filename)
|
|
19
18
|
end
|
20
19
|
|
21
20
|
def define_macro(name, *args, &block)
|
22
|
-
|
21
|
+
Project.value[:macros][name] = {args: args, block: block}
|
23
22
|
end
|
24
23
|
alias def_macro define_macro
|
25
24
|
alias defmacro define_macro
|
@@ -28,13 +27,12 @@ alias defmacro define_macro
|
|
28
27
|
# @param name (String) Group name
|
29
28
|
# @param block (Block) Tests code
|
30
29
|
def group(name, &block)
|
31
|
-
|
30
|
+
Project.value[:groups] << {name: name, block: block}
|
32
31
|
end
|
33
32
|
alias task group
|
34
33
|
|
35
|
-
# Start test
|
36
|
-
# @param block (Block) Extra code executed at the end.
|
37
34
|
def play(&block)
|
38
|
-
|
35
|
+
# Start test
|
36
|
+
CaseManager.new.play(&block)
|
39
37
|
end
|
40
38
|
alias start play
|