teuton 2.1.11 → 2.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +15 -12
  3. data/bin/check_teuton +0 -2
  4. data/docs/changelog/ideas.md +132 -0
  5. data/docs/changelog/v2.1.md +14 -122
  6. data/docs/changelog/v2.2.md +52 -28
  7. data/docs/changelog/version2.1.md +4 -0
  8. data/docs/commands/README.md +58 -15
  9. data/docs/commands/example_check.md +0 -4
  10. data/docs/commands/example_run.md +0 -4
  11. data/docs/dsl/README.md +1 -1
  12. data/docs/dsl/definition/result.md +1 -0
  13. data/docs/dsl/definition/run_remote.md +12 -6
  14. data/docs/dsl/definition/target.md +9 -10
  15. data/docs/install/README.md +13 -18
  16. data/docs/install/vagrant_docker.md +1 -1
  17. data/docs/learn/README.md +11 -8
  18. data/docs/learn/example-01-target.md +25 -25
  19. data/docs/learn/example-02-config.md +38 -49
  20. data/docs/learn/example-03-remote-hosts.md +22 -22
  21. data/docs/learn/{example-11-first-test.md → example-04-new-test.md} +23 -24
  22. data/docs/learn/{example-04-use.md → example-05-use.md} +6 -6
  23. data/docs/learn/{example-05-debug.md → example-06-debug.md} +8 -8
  24. data/docs/learn/{example-06-log.md → example-07-log.md} +7 -7
  25. data/docs/learn/{example-07-readme.md → example-08-readme.md} +10 -10
  26. data/docs/learn/{example-08-preserve.md → example-09-preserve.md} +6 -6
  27. data/docs/videos.md +19 -0
  28. data/lib/teuton/application.rb +22 -3
  29. data/lib/teuton/case_manager/case/dsl/goto.rb +2 -2
  30. data/lib/teuton/case_manager/case/dsl/macro.rb +1 -0
  31. data/lib/teuton/case_manager/case/dsl/send.rb +2 -1
  32. data/lib/teuton/case_manager/case/play.rb +2 -0
  33. data/lib/teuton/case_manager/case/result/ext_compare.rb +16 -0
  34. data/lib/teuton/case_manager/case/result/result.rb +1 -1
  35. data/lib/teuton/case_manager/case/runner.rb +31 -4
  36. data/lib/teuton/case_manager/case_manager.rb +1 -1
  37. data/lib/teuton/case_manager/export_manager.rb +21 -11
  38. data/lib/teuton/case_manager/utils.rb +1 -1
  39. data/lib/teuton/{project/laboratory → check}/builtin.rb +0 -0
  40. data/lib/teuton/{project/laboratory → check}/dsl.rb +40 -28
  41. data/lib/teuton/{project/laboratory → check}/laboratory.rb +3 -8
  42. data/lib/teuton/{project/laboratory → check}/show.rb +53 -59
  43. data/lib/teuton/cli.rb +85 -14
  44. data/lib/teuton/{project/readme → readme}/dsl.rb +0 -0
  45. data/lib/teuton/{project/readme → readme}/lang.rb +1 -1
  46. data/lib/teuton/{project/readme → readme}/readme.rb +22 -18
  47. data/lib/teuton/report/formatter/array_formatter.rb +13 -1
  48. data/lib/teuton/report/formatter/base_formatter.rb +18 -5
  49. data/lib/teuton/{project/skeleton.rb → skeleton.rb} +7 -19
  50. data/lib/teuton/utils/configfile_reader.rb +121 -0
  51. data/lib/teuton/{project → utils}/name_file_finder.rb +46 -26
  52. data/lib/teuton/version.rb +8 -0
  53. data/lib/teuton.rb +30 -32
  54. metadata +107 -65
  55. data/lib/teuton/cli/check.rb +0 -38
  56. data/lib/teuton/cli/main.rb +0 -6
  57. data/lib/teuton/cli/play.rb +0 -38
  58. data/lib/teuton/cli/readme.rb +0 -26
  59. data/lib/teuton/cli/version.rb +0 -12
  60. data/lib/teuton/files/gitignore +0 -2
  61. data/lib/teuton/project/configfile_reader.rb +0 -49
  62. data/lib/teuton/project/project.rb +0 -80
@@ -1,8 +1,8 @@
1
1
  require 'terminal-table'
2
2
  require 'rainbow'
3
3
 
4
- require_relative '../../application'
5
- require_relative '../configfile_reader'
4
+ require_relative '../application'
5
+ require_relative '../utils/configfile_reader'
6
6
 
7
7
  # Laboratory
8
8
  # * show_dsl
@@ -10,68 +10,23 @@ require_relative '../configfile_reader'
10
10
  # * show_config
11
11
  class Laboratory
12
12
  ##
13
- # Display DSL on screen
14
- def show_dsl
13
+ # Display DSL checking on screen
14
+ def show
15
15
  @verbose = true
16
16
  process_content
17
17
  show_stats
18
- show_config
19
- end
20
-
21
- ##
22
- # Display stats on screen
23
- def show_stats
24
- @stats[:hosts] = 0
25
- @hosts.each_pair { |_k, v| @stats[:hosts] += v }
26
-
27
- my_screen_table = Terminal::Table.new do |st|
28
- st.add_row ['DSL Stats', 'Count']
29
- st.add_separator
30
- st.add_row ['Groups', @stats[:groups]]
31
- st.add_row ['Targets', @stats[:targets]]
32
- st.add_row ['Goto', @stats[:hosts]]
33
- @hosts.each_pair { |k, v| st.add_row [" * #{k}", v] }
34
- st.add_row ['Uniques', @stats[:uniques]]
35
- st.add_row ['Logs', @stats[:uniques]]
36
- st.add_row [' ', ' ']
37
-
38
- st.add_row ['Gets', @stats[:gets]]
39
- if @gets.count > 0
40
- list = @gets.sort_by { |_k, v| v }
41
- list.reverse_each { |item| st.add_row [" * #{item[0]}", item[1].to_s] }
42
- end
43
-
44
- st.add_row ['Sets', @stats[:sets]]
45
- if @sets.count > 0
46
- @sets.each_pair { |k, v| st.add_row [" * #{k}", v.to_s] }
47
- end
48
- end
49
- verboseln my_screen_table.to_s + "\n"
18
+ revise_config_content
50
19
  end
51
20
 
52
21
  ##
53
- # Display config on screen
54
- def show_config
22
+ # Display config for teuton panel on screen
23
+ def show_panelconfig
55
24
  @verbose = false
56
25
  process_content
57
26
  @verbose = true
58
- revise_config_content
27
+ recomended_panelconfig_content
59
28
  end
60
29
 
61
- # def show_requests
62
- # @verbose = false
63
- # process_content
64
- # @verbose = true
65
- # my_screen_table = Terminal::Table.new do |st|
66
- # st.add_row ['Lines', 'REQUEST description']
67
- # st.add_separator
68
- # @requests.each_with_index do |line, index|
69
- # st.add_row ['%03d' % index, line]
70
- # end
71
- # end
72
- # verboseln my_screen_table
73
- # end
74
-
75
30
  private
76
31
 
77
32
  def verbose(text)
@@ -84,14 +39,16 @@ class Laboratory
84
39
 
85
40
  def process_content
86
41
  groups = Application.instance.groups
42
+ option = Application.instance.options
43
+
87
44
  verboseln ''
88
45
  groups.each do |t|
89
46
  @stats[:groups] += 1
90
-
91
- msg = "GROUP: #{t[:name]}"
92
- my_screen_table = Terminal::Table.new { |st| st.add_row [msg] }
93
- verboseln my_screen_table
94
-
47
+ unless option[:panel]
48
+ msg = "GROUP: #{t[:name]}"
49
+ my_screen_table = Terminal::Table.new { |st| st.add_row [msg] }
50
+ verboseln my_screen_table
51
+ end
95
52
  instance_eval(&t[:block])
96
53
  end
97
54
  end
@@ -127,10 +84,16 @@ class Laboratory
127
84
  verboseln YAML.dump(output)
128
85
  end
129
86
 
87
+ def recomended_panelconfig_content
88
+ output = { global: {}, cases: nil }
89
+ script_vars = find_script_vars
90
+ script_vars.each { |i| output[:global][i] = 'VALUE' }
91
+ verboseln YAML.dump(output)
92
+ end
93
+
130
94
  ##
131
95
  # Revive and check config content
132
96
  def revise_config_content
133
- @verbose = true
134
97
  my_screen_table = Terminal::Table.new do |st|
135
98
  st.add_row ['Revising CONFIG file']
136
99
  end
@@ -166,4 +129,35 @@ class Laboratory
166
129
  end
167
130
  end
168
131
  end
132
+
133
+ ##
134
+ # Display stats on screen
135
+ def show_stats
136
+ @stats[:hosts] = 0
137
+ @hosts.each_pair { |_k, v| @stats[:hosts] += v }
138
+
139
+ my_screen_table = Terminal::Table.new do |st|
140
+ st.add_row ['DSL Stats', 'Count']
141
+ st.add_separator
142
+ st.add_row ['Groups', @stats[:groups]]
143
+ st.add_row ['Targets', @stats[:targets]]
144
+ st.add_row ['Goto', @stats[:hosts]]
145
+ @hosts.each_pair { |k, v| st.add_row [" * #{k}", v] }
146
+ st.add_row ['Uniques', @stats[:uniques]]
147
+ st.add_row ['Logs', @stats[:uniques]]
148
+ st.add_row [' ', ' ']
149
+
150
+ st.add_row ['Gets', @stats[:gets]]
151
+ if @gets.count > 0
152
+ list = @gets.sort_by { |_k, v| v }
153
+ list.reverse_each { |item| st.add_row [" * #{item[0]}", item[1].to_s] }
154
+ end
155
+
156
+ st.add_row ['Sets', @stats[:sets]]
157
+ if @sets.count > 0
158
+ @sets.each_pair { |k, v| st.add_row [" * #{k}", v.to_s] }
159
+ end
160
+ end
161
+ verboseln my_screen_table.to_s + "\n"
162
+ end
169
163
  end
data/lib/teuton/cli.rb CHANGED
@@ -1,39 +1,110 @@
1
+
1
2
  require 'thor'
2
- require_relative 'application'
3
- require_relative 'project/project'
4
- require_relative 'project/skeleton.rb'
5
- require_relative 'cli/main'
3
+ require_relative 'version'
4
+ require_relative '../teuton'
6
5
 
7
- ##
8
- # Command Line User Interface
9
6
  class CLI < Thor
10
7
  map ['h', '-h', '--help'] => 'help'
11
8
 
12
- map ['c', '-c', '--create', 'create'] => 'new'
9
+ map ['v', '-v', '--version'] => 'version'
10
+ desc 'version', 'Show the program version'
11
+ def version
12
+ puts "#{Teuton::APPNAME} (version #{Teuton::VERSION})"
13
+ end
14
+
15
+ map ['n', '-n', '--new'] => 'new'
13
16
  desc 'new DIRECTORY', 'Create skeleton for a new project'
14
17
  long_desc <<-LONGDESC
15
18
  Create files for a new project.
19
+ LONGDESC
20
+ def new(projectpath)
21
+ Teuton.create(projectpath)
22
+ end
23
+
24
+ map ['c', '-c', '--check'] => 'check'
25
+ option :panelconfig, type: :boolean
26
+ option :cname, type: :string
27
+ option :cpath, type: :string
28
+ desc 'check [OPTIONS] DIRECTORY',
29
+ 'Check test and config file content'
30
+ long_desc <<-LONGDESC
31
+
32
+ (1) teuton check path/to/dir/foo
33
+ , Check content of start.rb and config.yaml files.
16
34
 
17
- Example:
35
+ (2) teuton check path/to/dir/foo --cname=demo
36
+ , Check content of start.rb and demo.yaml files.
37
+
38
+ (3) teuton check path/to/file/foo.rb
39
+ , Check content of foo.rb and foo.yaml files.
40
+
41
+ (4) teuton check path/to/file/foo.rb --cname=demo
42
+ , Check content of foo.rb and demo.yaml files.
18
43
 
19
- #{$PROGRAM_NAME} create dir/foo
20
44
  LONGDESC
21
- ##
22
- # Command: create new Teuton project
23
- def new(path_to_new_dir)
24
- Skeleton.create(path_to_new_dir)
45
+ def check(projectpath)
46
+ Teuton.check(projectpath, options)
47
+ end
48
+
49
+ map ['--run', 'run'] => 'play'
50
+ option :export, type: :string
51
+ option :cname, type: :string
52
+ option :cpath, type: :string
53
+ option :case, type: :string
54
+ option :color, type: :boolean
55
+ option :quiet, type: :boolean
56
+ desc '[run] [OPTIONS] DIRECTORY',
57
+ 'Run test from directory'
58
+ long_desc <<-LONGDESC
59
+ This function execute challenge from specified directory.
60
+ By default, show progress on the screen.
61
+
62
+ Let's see others options:
63
+
64
+ (1) teuton foo, run challenge from foo/start.rb with foo/config.yaml config file.
65
+
66
+ (2) teuton run foo, same as (1).
67
+
68
+ (3) teuton run --export=json foo, run challenge and export using json format.
69
+ Others output formats availables are: txt, html, yaml, json and colored_text.
70
+
71
+ (4) teuton run --cname=demo foo, run challenge from foo/start.rb with foo/demo.yaml config file.
72
+
73
+ (5) teuton foo/demo.rb, Run challenge from foo/demo.rb with foo/demo.yaml config file.
74
+
75
+ LONGDESC
76
+ def play(filepath)
77
+ Teuton.run(filepath, options)
78
+ end
79
+
80
+ map ['--readme'] => 'readme'
81
+ option :lang, type: :string
82
+ desc 'readme DIRECTORY',
83
+ 'Show README extracted from test contents'
84
+ long_desc <<-LONGDESC
85
+
86
+ (1) teuton readme foo
87
+ , Create README.md from foo/start.rb.
88
+
89
+ (2) teuton readme foo/demo.rb
90
+ , Create README.md from foo/demo.rb.
91
+ LONGDESC
92
+
93
+ def readme(projectpath)
94
+ # Create README from teuton test
95
+ Teuton.readme(projectpath, options)
25
96
  end
26
97
 
27
98
  ##
28
99
  # These inputs are equivalents:
29
100
  # * teuton dir/foo
30
101
  # * teuton run dir/foo
31
- # * teuton play dir/foo
32
102
  def method_missing(method, *_args, &_block)
33
103
  play(method.to_s)
34
104
  end
35
105
 
36
106
  def respond_to_missing?(method_name, include_private = false)
107
+ # Respond to missing methods name
37
108
  super
38
109
  end
39
110
  end
File without changes
@@ -1,5 +1,5 @@
1
1
 
2
- require_relative '../../application'
2
+ require_relative '../application'
3
3
 
4
4
  module Lang
5
5
  def self.get(key)
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../../application'
4
- require_relative '../configfile_reader'
5
- require_relative '../../case_manager/case/result/result'
3
+ require_relative '../application'
4
+ require_relative '../utils/configfile_reader'
5
+ require_relative '../case_manager/case/result/result'
6
6
  require_relative 'dsl'
7
7
  require_relative 'lang'
8
8
 
@@ -44,6 +44,19 @@ class Readme
44
44
  reset
45
45
  end
46
46
 
47
+ ##
48
+ # Show README on screen
49
+ def show
50
+ process_content
51
+ show_head
52
+ show_content
53
+ show_tail
54
+ end
55
+
56
+ private
57
+
58
+ ##
59
+ # Reset attributes
47
60
  def reset
48
61
  app = Application.instance
49
62
  @config = ConfigFileReader.read(app.config_path)
@@ -73,13 +86,6 @@ class Readme
73
86
  @action = { readme: [] }
74
87
  end
75
88
 
76
- def show
77
- process_content
78
- show_head
79
- show_content
80
- show_tail
81
- end
82
-
83
89
  ##
84
90
  # Show README head
85
91
  def show_head
@@ -97,23 +103,21 @@ class Readme
97
103
  puts Lang::get(:hosts)
98
104
  puts "\n"
99
105
  puts '| ID | Host | Configuration |'
100
- puts '| -- | ---- | ------------- |'
106
+ puts '| --- | --- | --- |'
101
107
  @required_hosts.each_pair do |k, v|
102
108
  c = []
103
109
  v.each_pair { |k2,v2| c << "#{k2}=#{v2}" }
104
- puts "|#{i}|#{k.upcase}|#{c.join(', ')}|"
110
+ puts "| #{i} | #{k.upcase} | #{c.join(', ')} |"
105
111
  i += 1
106
112
  end
107
113
  puts "\n> NOTE: SSH Service installation is required on every host."
108
114
  end
109
115
 
110
116
  unless @cases_params.empty?
111
- @cases_params.uniq!.sort!
117
+ @cases_params.sort!
112
118
  puts Lang::get(:params)
113
119
  @cases_params.uniq.each { |i| puts format('* %s', i) }
114
- puts "\n> NOTE:"
115
- puts "> * Teuton software must known this information!"
116
- puts "> * Save every ':param: value' into config file."
120
+ puts "\n> NOTE: Save every ':param: value' into config file."
117
121
  end
118
122
  end
119
123
 
@@ -156,13 +160,13 @@ class Readme
156
160
  puts Lang::get(:global)
157
161
  puts "\n"
158
162
  puts '| Global param | Value |'
159
- puts '| ------------ | ----- |'
163
+ puts '| --- | --- |'
160
164
  @global_params.each_pair { |k,v| puts "|#{k}|#{v}|" }
161
165
  puts "\n\#\# Created params"
162
166
  puts Lang::get(:created)
163
167
  puts "\n"
164
168
  puts '| Created params | Value |'
165
- puts '| -------------- | ----- |'
169
+ puts '| --- | --- |'
166
170
  @setted_params.each_pair { |k,v| puts "|#{k}|#{v}|" }
167
171
  end
168
172
  end
@@ -2,13 +2,19 @@
2
2
 
3
3
  require_relative 'base_formatter'
4
4
 
5
+ ##
5
6
  # ArrayFormatter class: format report data into an array
6
7
  class ArrayFormatter < BaseFormatter
8
+ ##
9
+ # Initialize class
10
+ # @param report (Report) Format report data into Array
7
11
  def initialize(report)
8
12
  super(report)
9
13
  @data = {}
10
14
  end
11
15
 
16
+ ##
17
+ # Execute format action
12
18
  def process
13
19
  build_data
14
20
  w @data.to_s # Write data into ouput file
@@ -28,6 +34,8 @@ class ArrayFormatter < BaseFormatter
28
34
  @data[:config] = head
29
35
  end
30
36
 
37
+ # rubocop:disable Metrics/AbcSize
38
+ # rubocop:disable Metrics/MethodLength
31
39
  def build_history_data
32
40
  @data[:logs] = []
33
41
  groups = []
@@ -46,7 +54,8 @@ class ArrayFormatter < BaseFormatter
46
54
  end
47
55
 
48
56
  target = {}
49
- target[:target_id] = format('%02d', i[:id])
57
+ # target[:target_id] = format('%02d', i[:id])
58
+ target[:target_id] = format('%<id>02d', id: i[:id])
50
59
  target[:check] = i[:check]
51
60
  target[:score] = value
52
61
  target[:weight] = i[:weight]
@@ -66,6 +75,8 @@ class ArrayFormatter < BaseFormatter
66
75
  groups << { title: title, targets: targets } unless title.nil?
67
76
  @data[:groups] = groups
68
77
  end
78
+ # rubocop:enable Metrics/AbcSize
79
+ # rubocop:enable Metrics/MethodLength
69
80
 
70
81
  def build_final_data
71
82
  tail = {}
@@ -77,6 +88,7 @@ class ArrayFormatter < BaseFormatter
77
88
  app = Application.instance
78
89
  @data[:hall_of_fame] = {}
79
90
  return if app.options[:case_number] < 3
91
+
80
92
  fame = {}
81
93
  app.hall_of_fame.each { |line| fame[line[0]] = line[1] }
82
94
  @data[:hall_of_fame] = fame
@@ -2,25 +2,38 @@
2
2
 
3
3
  # BaseFormatter class
4
4
  class BaseFormatter
5
+ ##
6
+ # Initialize class
7
+ # @param report (Report) Format report data
5
8
  def initialize(report)
6
9
  @head = report.head
7
10
  @lines = report.lines
8
11
  @tail = report.tail
9
12
  end
10
13
 
14
+ ##
15
+ # Execute format action
16
+ def process
17
+ raise 'Empty method!'
18
+ end
19
+
20
+ ##
21
+ # Creates new output file
22
+ # @param filename (String) Path to output file
11
23
  def init(filename)
12
24
  @filename = filename
13
25
  @file = File.open(@filename, 'w')
14
26
  end
15
27
 
28
+ ##
29
+ # Write data into output file
30
+ # @param text (String) Text data to write into output file
16
31
  def w(text)
17
- @file.write text.to_s # write into output file
18
- end
19
-
20
- def process
21
- raise 'Empty method!'
32
+ @file.write text.to_s
22
33
  end
23
34
 
35
+ ##
36
+ # Close open output file
24
37
  def deinit
25
38
  @file.close
26
39
  end
@@ -3,13 +3,7 @@
3
3
  require 'fileutils'
4
4
  require 'rainbow'
5
5
 
6
- # Skeleton class
7
- # * create
8
- # * create_main_dir_and_files
9
- # * create_assets_dir_and_files
10
- # * create_dir
11
- # * create_dirs
12
- # * copyfile
6
+ # Skeleton module
13
7
  module Skeleton
14
8
  ##
15
9
  # Create teuton project skeleton
@@ -17,7 +11,7 @@ module Skeleton
17
11
  def self.create(project_dir)
18
12
  project_name = File.basename(project_dir)
19
13
  puts "\n[INFO] Creating #{Rainbow(project_name).bright} project skeleton"
20
- source_basedir = File.join(File.dirname(__FILE__), '..')
14
+ source_basedir = File.dirname(__FILE__)
21
15
  create_dir project_dir
22
16
  create_main_dir_and_files(project_dir, source_basedir)
23
17
  end
@@ -26,12 +20,11 @@ module Skeleton
26
20
  # Create main dir and files
27
21
  # @param project_dir (String)
28
22
  # @param source_basedir (String)
29
- def self.create_main_dir_and_files(project_dir, source_basedir)
23
+ private_class_method def self.create_main_dir_and_files(project_dir, source_basedir)
30
24
  # Directory and files: Ruby script, Configfile, gitignore
31
25
  items = [
32
26
  { source: 'files/config.yaml', target: 'config.yaml' },
33
27
  { source: 'files/start.rb', target: 'start.rb' }
34
- # { source: 'files/gitignore', target: '.gitignore' }
35
28
  ]
36
29
  items.each do |item|
37
30
  source = File.join(source_basedir, item[:source])
@@ -43,7 +36,7 @@ module Skeleton
43
36
  ##
44
37
  # Create dir
45
38
  # @param dirpath (String)
46
- def self.create_dir(dirpath)
39
+ private_class_method def self.create_dir(dirpath)
47
40
  if Dir.exist? dirpath
48
41
  puts "* Exists dir! => #{Rainbow(dirpath).yellow}"
49
42
  else
@@ -56,18 +49,12 @@ module Skeleton
56
49
  end
57
50
  end
58
51
 
59
- ##
60
- # Create dirs
61
- # @param args (Array)
62
- def self.create_dirs(*args)
63
- args.each { |arg| create_dir arg }
64
- end
65
-
66
52
  ##
67
53
  # Copy file
68
54
  # @param source (String) Source file
69
55
  # @param dest (String) Dest file
70
- def self.copyfile(source, dest)
56
+ # rubocop:disable Metrics/MethodLength
57
+ private_class_method def self.copyfile(source, dest)
71
58
  if File.exist? dest
72
59
  puts "* Exists file! => #{Rainbow(dest).yellow}"
73
60
  else
@@ -80,4 +67,5 @@ module Skeleton
80
67
  end
81
68
  end
82
69
  end
70
+ # rubocop:enable Metrics/MethodLength
83
71
  end
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+ require 'json/pure'
5
+
6
+ ##
7
+ # Functions that read data from ConfigFile using YAML or JSON formats
8
+ module ConfigFileReader
9
+ ##
10
+ # Read config file
11
+ # @param filepath (String) Path to config file
12
+ # @return Hash with config data
13
+ def self.read(filepath)
14
+ unless File.exist?(filepath)
15
+ data = {}
16
+ data[:global] = {}
17
+ data[:alias] = {}
18
+ data[:cases] = [{ tt_members: 'anonymous' }]
19
+ return data
20
+ end
21
+ return read_yaml(filepath) if File.extname(filepath) == '.yaml'
22
+
23
+ return read_json(filepath) if File.extname(filepath) == '.json'
24
+
25
+ raise "[ERROR] ConfigFileReader: #{filepath}"
26
+ end
27
+
28
+ ##
29
+ # Read YAML config file
30
+ # @param filepath (String) Path to YAML config file
31
+ # @return Hash with config data
32
+ # rubocop:disable Metrics/MethodLength
33
+ # rubocop:disable Metrics/AbcSize
34
+ # rubocop:disable Security/YAMLLoad
35
+ def self.read_yaml(filepath)
36
+ begin
37
+ data = YAML.load(File.open(filepath))
38
+ rescue StandardError => e
39
+ puts "\n" + ('=' * 80)
40
+ puts "[ERROR] ConfigFileReader#read <#{filepath}>"
41
+ puts ' I suggest to revise file format!'
42
+ puts " #{e.message}\n" + ('=' * 80)
43
+ raise "[ERROR] ConfigFileReader <#{e}>"
44
+ end
45
+ data = convert_string_keys_to_symbol(data)
46
+ data[:global] = data[:global] || {}
47
+ data[:alias] = data[:alias] || {}
48
+ data[:cases] = data[:cases] || []
49
+ read_included_files!(filepath, data)
50
+ data
51
+ end
52
+ # rubocop:enable Metrics/MethodLength
53
+ # rubocop:enable Metrics/AbcSize
54
+ # rubocop:enable Security/YAMLLoad
55
+
56
+ ##
57
+ # Read JSON config file
58
+ # @param filepath (String) Path to JSON config file
59
+ # @return Hash with config data
60
+ def self.read_json(filepath)
61
+ data = JSON.parse(File.read(filepath), symbolize_names: true)
62
+ data = convert_string_keys_to_symbol(data)
63
+ data[:global] = data[:global] || {}
64
+ data[:alias] = data[:alias] || {}
65
+ data[:cases] = data[:cases] || []
66
+ read_included_files!(filepath, data)
67
+ data
68
+ end
69
+
70
+ ##
71
+ # Read all configuration files from "filepath" folder.
72
+ # @param filepath (String) Folder with config files
73
+ # @param data (Hash) Input configuration
74
+ # rubocop:disable Security/YAMLLoad
75
+ private_class_method def self.read_included_files!(filepath, data)
76
+ return if data[:global][:tt_include].nil?
77
+
78
+ include_dir = data[:global][:tt_include]
79
+ if include_dir == File.absolute_path(include_dir)
80
+ basedir = include_dir
81
+ else
82
+ basedir = File.join(File.dirname(filepath), data[:global][:tt_include])
83
+ end
84
+ files = Dir.glob(File.join(basedir, '**/*.yaml'))
85
+ files += Dir.glob(File.join(basedir, '**/*.yml'))
86
+ files.each { |file|
87
+ begin
88
+ data[:cases] << YAML.load(File.open(file))
89
+ rescue StandardError => e
90
+ puts "\n" + ('=' * 80)
91
+ puts "[ERROR] ConfigFileReader#read <#{file}>"
92
+ puts ' I suggest to revise file format!'
93
+ puts " #{e.message}\n" + ('=' * 80)
94
+ end
95
+ }
96
+ end
97
+ # rubocop:enable Security/YAMLLoad
98
+
99
+ # rubocop:disable Metrics/MethodLength
100
+ # rubocop:disable Metrics/AbcSize
101
+ private_class_method def self.convert_string_keys_to_symbol(input)
102
+ return input if input.class != Hash
103
+
104
+ output = {}
105
+ input.each_pair do |key, value|
106
+ key2 = key
107
+ key2 = key.to_sym if key.class
108
+ value2 = value
109
+ if value.class == Hash
110
+ value2 = convert_string_keys_to_symbol(value)
111
+ elsif value.class == Array
112
+ value2 = []
113
+ value.each { |i| value2 << convert_string_keys_to_symbol(i) }
114
+ end
115
+ output[key2] = value2
116
+ end
117
+ output
118
+ end
119
+ # rubocop:enable Metrics/MethodLength
120
+ # rubocop:enable Metrics/AbcSize
121
+ end