cem_win_spec 0.1.6 → 0.1.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0c99510b91f69ac85f4e529d51134f797546bca8ee7ede74629a08620280b4c6
4
- data.tar.gz: 57aa64d9931b08ce6a1e49178303643ded631334b43612707ee7383fb8c07426
3
+ metadata.gz: 000454f69f3ea2722dcd11ec105a5ebe617301d076a2c1772cb530d8275e0f11
4
+ data.tar.gz: 1d2635cc5f330baa755482c5738901e2e79aeabd585200db5ee0ef218cd9decf
5
5
  SHA512:
6
- metadata.gz: bdc2ba16b32de3f4e5eff3f327d5407cb7d377cb836ae971b9c7dd57b771d8b1e7d7347c312006e52e77302c8ff2c61c899d5f82e39336aefa8bfb41dca0e865
7
- data.tar.gz: 07f97ff0b736be471fea8cd9db08ce10c070353e03c5e523fc301b75a39a6689e77149ea95933d55c7cda5110f93983e562305bf4020472025fadde500293a31
6
+ metadata.gz: d8d28e53eeb77001c89b81a9140be2783278c528fa1dc92aef59b7154cd0174fba47b534d346977135e9465115b74981d9bfdd04eb35952a612e80dea71657a4
7
+ data.tar.gz: 0d4b7117383ab02ee03c0d8f19f75be611a3e678dd973140844f9316d651db7bc0263ed0f4354fda64893b498a344128adb6e44dcb999030bef66445dc4cd0f9
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cem_win_spec (0.1.6)
4
+ cem_win_spec (0.1.8)
5
5
  parallel_tests (~> 3.4)
6
6
  puppet_forge (~> 4.1)
7
7
  winrm (~> 2.3)
@@ -11,31 +11,40 @@ GEM
11
11
  remote: https://rubygems.org/
12
12
  specs:
13
13
  ast (2.4.2)
14
- builder (3.2.4)
14
+ base64 (0.2.0)
15
+ bigdecimal (3.1.8)
16
+ builder (3.3.0)
15
17
  coderay (1.1.3)
16
18
  diff-lcs (1.5.0)
17
- erubi (1.12.0)
18
- faraday (2.7.10)
19
- faraday-net_http (>= 2.0, < 3.1)
20
- ruby2_keywords (>= 0.0.4)
19
+ erubi (1.13.0)
20
+ faraday (2.12.1)
21
+ faraday-net_http (>= 2.0, < 3.5)
22
+ json
23
+ logger
21
24
  faraday-follow_redirects (0.3.0)
22
25
  faraday (>= 1, < 3)
23
- faraday-net_http (3.0.2)
24
- ffi (1.15.5)
26
+ faraday-net_http (3.4.0)
27
+ net-http (>= 0.5.0)
28
+ ffi (1.16.3)
25
29
  gssapi (1.3.1)
26
30
  ffi (>= 1.0.1)
27
31
  gyoku (1.4.0)
28
32
  builder (>= 2.1.2)
29
33
  rexml (~> 3.0)
30
34
  httpclient (2.8.3)
35
+ json (2.8.2)
31
36
  little-plugger (1.1.4)
32
- logging (2.3.1)
37
+ logger (1.6.5)
38
+ logging (2.4.0)
33
39
  little-plugger (~> 1.1)
34
40
  multi_json (~> 1.14)
35
41
  method_source (1.0.0)
36
- minitar (0.9)
42
+ minitar (0.12.1)
37
43
  multi_json (1.15.0)
38
- nori (2.6.0)
44
+ net-http (0.5.0)
45
+ uri
46
+ nori (2.7.1)
47
+ bigdecimal
39
48
  parallel (1.22.1)
40
49
  parallel_tests (3.13.0)
41
50
  parallel
@@ -78,19 +87,21 @@ GEM
78
87
  rubocop-ast (1.24.1)
79
88
  parser (>= 3.1.1.0)
80
89
  ruby-progressbar (1.11.0)
81
- ruby2_keywords (0.0.5)
82
- rubyntlm (0.6.3)
90
+ rubyntlm (0.6.5)
91
+ base64
83
92
  rubyzip (2.3.2)
84
93
  semantic_puppet (1.1.0)
85
94
  unicode-display_width (2.1.0)
86
- winrm (2.3.6)
95
+ uri (1.0.2)
96
+ winrm (2.3.9)
87
97
  builder (>= 2.1.2)
88
98
  erubi (~> 1.8)
89
99
  gssapi (~> 1.2)
90
100
  gyoku (~> 1.0)
91
101
  httpclient (~> 2.2, >= 2.2.0.2)
92
102
  logging (>= 1.6.1, < 3.0)
93
- nori (~> 2.0)
103
+ nori (~> 2.0, >= 2.7.1)
104
+ rexml (~> 3.0)
94
105
  rubyntlm (~> 0.6.0, >= 0.6.3)
95
106
  winrm-fs (1.3.5)
96
107
  erubi (~> 1.8)
data/exe/cem-win-spec CHANGED
@@ -77,3 +77,4 @@ parser.parse!
77
77
 
78
78
  operation = options[:operation] || :spec
79
79
  CemWinSpec.run(operation, options)
80
+ exit CemWinSpec.exit_code
@@ -98,7 +98,7 @@ namespace 'cem_win_spec' do
98
98
  puts "Copying Hiera config to #{hiera_fix}..."
99
99
  ::FileUtils.cp(cem_win_spec_hiera_conf, hiera_fix)
100
100
  raise 'Hiera config copy failed!' unless File.exist?(hiera_fix)
101
-
101
+
102
102
  puts 'Spec data prepared successfully'
103
103
  puts 'Setting up module fixture cache...'
104
104
  cache = CemWinSpec::FixtureCache.new
@@ -21,19 +21,11 @@ module CemWinSpec
21
21
  @rspec_cmds = RspecTestCmds.new
22
22
  @iap_tunnel = IapTunnel.new
23
23
  @win_exec_factory = CemWinSpec::WinExec::Factory.new(@iap_tunnel, @module_archive_builder, @rspec_cmds)
24
+ @retry = false
24
25
  end
25
26
 
26
- def run_all
27
- check_connectivity.run
28
- enable_long_paths.run
29
- enable_symlinks.run
30
- @working_dir = create_working_dir.run
31
- upload_module.run(@working_dir)
32
- setup_ruby.run(@working_dir)
33
- rspec_tests_parallel.run(@working_dir)
34
- ensure
35
- clean_up(@working_dir)
36
- @iap_tunnel.stop if @iap_tunnel.running?
27
+ def retry?
28
+ @retry
37
29
  end
38
30
 
39
31
  def enable_long_paths(**opts)
@@ -78,6 +70,8 @@ module CemWinSpec
78
70
  @setup_ruby ||= new_command('Set up ruby', **opts) do
79
71
  remote_run(
80
72
  [
73
+ 'if (Test-Path .bundle) { Remove-Item -Recurse -Force .bundle }',
74
+ 'if (Test-Path Gemfile.lock) { Remove-Item -Force Gemfile.lock }',
81
75
  'bundle config disable_platform_warnings true',
82
76
  'bundle config set --local without \'local_development\'',
83
77
  'bundle install',
@@ -104,13 +98,17 @@ module CemWinSpec
104
98
  end
105
99
  end
106
100
 
107
- def clean_up
108
- @clean_up ||= new_command('Cleanup') do |working_dir|
109
- if remote_available?
110
- remote_run(cleanup_cmd, quiet: true)
111
- else
112
- logger.warn 'Cleanup not available'
113
- end
101
+ def clean_up(**opts)
102
+ @clean_up ||= new_command('Cleanup', **opts) do
103
+ remote_run(
104
+ [
105
+ "Get-ChildItem -Path #{remote_working_dir} -Force -Recurse | Remove-Item -Force -Recurse",
106
+ "Remove-Item #{remote_working_dir} -Force",
107
+ ],
108
+ ruby_cmd: false,
109
+ add_envs: false,
110
+ chdir: false,
111
+ )
114
112
  end
115
113
  end
116
114
 
@@ -120,6 +118,10 @@ module CemWinSpec
120
118
  end
121
119
  end
122
120
 
121
+ def inspect
122
+ "#<#{self.class.name}:0x#{object_id.to_s(16)}>"
123
+ end
124
+
123
125
  private
124
126
 
125
127
  def title_sym(title)
@@ -144,12 +146,5 @@ module CemWinSpec
144
146
  end
145
147
  end
146
148
  end
147
-
148
- def cleanup_cmd(working_dir)
149
- <<~EOS
150
- Get-ChildItem #{working_dir} -Recurse | Remove-Item -Force
151
- Remove-Item -Force #{working_dir}
152
- EOS
153
- end
154
149
  end
155
150
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CemWinSpec
4
- VERSION = "0.1.6"
4
+ VERSION = "0.1.8"
5
5
  end
@@ -12,6 +12,10 @@ module CemWinSpec
12
12
  '7' => '278',
13
13
  '8' => '322',
14
14
  }.freeze
15
+ TOOL_DIR_BY_RUBY_VER = {
16
+ '278' => 'C:/tools/ruby/2.7.8',
17
+ '322' => 'C:/tools/ruby/3.2.2',
18
+ }.freeze
15
19
 
16
20
  attr_accessor :working_dir, :env_vars, :puppet_version
17
21
 
@@ -45,6 +49,7 @@ module CemWinSpec
45
49
 
46
50
  value['PUPPET_GEM_VERSION'] = "~> #{puppet_version}" if puppet_version
47
51
  value['FACTER_GEM_VERSION'] = 'https://github.com/puppetlabs/facter#main' if puppet_version
52
+ value.delete('password') if value.key?('password')
48
53
  @env_vars = value
49
54
  end
50
55
 
@@ -65,27 +70,40 @@ module CemWinSpec
65
70
  PUPPET_VER_TO_RUBY_VER[puppet_version.split('.')[0]]
66
71
  end
67
72
 
68
- def command(cmd)
73
+ # Formats the command to be run
74
+ # @param cmd [String, Array[String]] the command to be run
75
+ # @param ruby_cmd [Boolean] Whether the command is a ruby command and should be ran
76
+ # in a ruby context. If false, the command will not set up the Uru ruby environment.
77
+ # @param add_envs [Boolean] Whether to add the environment variables to the command
78
+ # @param chdir [Boolean] Whether to change the working directory to the working dir
79
+ # @return [String] the formatted command
80
+ def command(cmd, ruby_cmd: true, add_envs: true, chdir: true, **_kwargs)
81
+ cmd = cmd.join(COMMAND_SEPARATOR) if cmd.is_a?(Array)
69
82
  cmd = [cmd]
70
- cmd.unshift(change_ruby_version_cmd) if ruby_version # executes third
71
- env_vars.each { |k, v| cmd.unshift(set_env_var_cmd(k, v)) } if env_vars.any? # executes second
72
- cmd.unshift(change_working_dir_cmd(working_dir)) if working_dir # executes first
83
+ cmd.unshift(change_ruby_version_cmd) if ruby_cmd && ruby_version # executes third
84
+ env_vars.each { |k, v| cmd.unshift(set_env_var_cmd(k, v)) } if add_envs && env_vars.any? # executes second
85
+ cmd.unshift(change_working_dir_cmd(working_dir)) if chdir && working_dir # executes first
73
86
  cmd.join(COMMAND_SEPARATOR)
74
87
  end
75
88
 
76
89
  private
77
90
 
78
91
  def log_command(cmd)
79
- cmd = command(cmd)
92
+ cmd.gsub!(/\$env:password = "\w+"/, '$env:password = [REDACTED]')
80
93
  logger.debug "Executing command:\n#{cmd.split(%r{\n|\r\n|;\s*}).map { |c| " #> #{c}" }.join("\n")}"
81
94
  end
82
95
 
96
+ # Commands that, when ran, will change the ruby version. This will also set the local bundle config path
83
97
  def change_ruby_version_cmd
84
- "uru #{ruby_version}"
98
+ [
99
+ "uru #{ruby_version}",
100
+ '$env:GEM_HOME = "$(ruby -e "puts Gem.dir")"',
101
+ 'bundle config set --local path "$env:GEM_HOME"',
102
+ ].join(COMMAND_SEPARATOR)
85
103
  end
86
104
 
87
105
  def set_env_var_cmd(key, value)
88
- "set #{key}=\"#{value}\""
106
+ "$env:#{key} = \"#{value}\""
89
107
  end
90
108
 
91
109
  def change_working_dir_cmd(dir)
@@ -35,9 +35,10 @@ module CemWinSpec
35
35
  # This is useful for running commands that should not block the current process
36
36
  # and that you don't need stdout/stderr from.
37
37
  # @param cmd [String] The command to execute
38
- def bg_run(cmd, *_args, **_kwargs)
38
+ def bg_run(cmd, *_args, **kwargs)
39
+ cmd = command(cmd, **kwargs)
39
40
  log_command(cmd)
40
- Process.detach(spawn(command(cmd)))
41
+ Process.detach(spawn())
41
42
  end
42
43
 
43
44
  # Execute a command in a new thread. This works by creating a new thread in a thread group
@@ -48,20 +49,22 @@ module CemWinSpec
48
49
  # Process::Status object.
49
50
  # @param cmd [String] The command to execute
50
51
  # @return [Array] An array of [:threaded, cmd]
51
- def thread_run(cmd, *_args, **_kwargs)
52
+ def thread_run(cmd, *_args, **kwargs)
52
53
  @ran_in_thread = true
53
54
  th = Thread.new do
55
+ cmd = command(cmd, **kwargs)
54
56
  log_command(cmd)
55
- so, se, st = Open3.capture3(command(cmd))
57
+ so, se, st = Open3.capture3(cmd)
56
58
  @thread_results[cmd] = [so, se, st]
57
59
  end
58
60
  thread_group.add th
59
61
  :threaded
60
62
  end
61
63
 
62
- def run(cmd, *_args, **_kwargs)
64
+ def run(cmd, *_args, **kwargs)
65
+ cmd = command(cmd, **kwargs)
63
66
  log_command(cmd)
64
- Open3.capture3(command(cmd))
67
+ Open3.capture3(cmd)
65
68
  end
66
69
 
67
70
  private
@@ -21,14 +21,22 @@ module CemWinSpec
21
21
  @available
22
22
  end
23
23
 
24
- def run(cmd, *_args, **_kwargs)
25
- cmd = cmd.join('; ') if cmd.is_a?(Array)
24
+ # Run a command over WinRM
25
+ # @param cmd [String] The command to run
26
+ # @param kwargs [Hash] Additional options for the command
27
+ # These following options are available:
28
+ # - :ruby_cmd [Boolean] Whether the command is a ruby command and should be ran
29
+ # in a ruby context. If false, the command will not set up the Uru ruby environment.
30
+ # - :add_envs [Boolean] Whether to add the environment variables to the command
31
+ # - :chdir [Boolean] Whether to change the working directory to the working dir
32
+ def run(cmd, *_args, **kwargs)
33
+ cmd = command(cmd, **kwargs)
26
34
  log_command(cmd)
27
35
  shell = nil
28
36
  output = nil
29
37
  begin
30
38
  shell = conn.shell(:powershell)
31
- output = shell.run(command(cmd)) do |stdout, stderr|
39
+ output = shell.run(cmd) do |stdout, stderr|
32
40
  logger << stdout if stdout
33
41
  logger << stderr if stderr
34
42
  end
@@ -36,7 +36,7 @@ module CemWinSpec
36
36
 
37
37
  def add_local_cmd(value)
38
38
  raise ArgumentError, 'local_exec must implement the #run method' unless value.respond_to?(:run)
39
-
39
+
40
40
  @local_cmd = value
41
41
  end
42
42
 
@@ -88,7 +88,7 @@ module CemWinSpec
88
88
  @ignore_exitcode = value
89
89
  end
90
90
  alias ignore_exitcode? ignore_exitcode
91
-
91
+
92
92
  def success?
93
93
  @result.success? if @result.is_a?(Output)
94
94
 
@@ -146,11 +146,13 @@ module CemWinSpec
146
146
  super
147
147
  end
148
148
  end
149
-
149
+
150
150
  def run(*args, **kwargs)
151
151
  validate_instance_variables
152
152
  logger.info "Running #{@title}"
153
153
  result = run_with_tunnel { run_in_current_scope(*args, **kwargs) }
154
+ logger.debug "Command stdout: #{result.stdout}" if result.respond_to?(:stdout)
155
+ logger.debug "Command stderr: #{result.stderr}" if result.respond_to?(:stderr)
154
156
  @result = Output.new(result)
155
157
  return if @result.pending_threaded?
156
158
 
@@ -35,12 +35,12 @@ module CemWinSpec
35
35
  # @return [Exec] An Exec object
36
36
  def build(title, merge: true, user: nil, pass: nil, working_dir: nil, **opts, &block)
37
37
  logger.debug "Building Wexec object for #{title}"
38
- build_conn_opts(merge: merge, user: user, pass: pass, **opts)
38
+ build_conn_opts(merge: merge, user: user, pass: pass, **@init_opts.merge(opts))
39
39
  logger.debug 'Created ConnectionOpts'
40
40
  wexec = Exec.new
41
41
  wexec.add_title title
42
42
  wexec.add_local_cmd @current_local_cmd
43
- wexec.add_remote_cmd get_remote_cmd(working_dir, **@init_opts.merge(opts))
43
+ wexec.add_remote_cmd get_remote_cmd(working_dir, **@current_conn_opts.to_h)
44
44
  wexec.add_iap_tunnel @iap_tunnel
45
45
  wexec.add_ma_builder @ma_builder
46
46
  wexec.add_rspec_test_cmds @rspec_test_cmds
@@ -68,7 +68,7 @@ module CemWinSpec
68
68
  @current_conn_opts = ConnectionOpts.new(new_host: 'localhost', new_port: @iap_tunnel.port, user: user, pass: pass, **opts)
69
69
  return @current_conn_opts
70
70
  end
71
-
71
+
72
72
  if need_new_conn_opts?(new_host: 'localhost', new_port: @iap_tunnel.port, user: user, pass: pass, **opts)
73
73
  if merge
74
74
  logger.debug 'Merging ConnectionOpts with new options'
@@ -53,19 +53,31 @@ module CemWinSpec
53
53
  end
54
54
  out_array.compact!
55
55
  return if out_array.empty?
56
-
56
+
57
57
  logger.info "#{stream_name.to_s.upcase}:\n#{out_array.join("\n")}"
58
58
  end
59
59
 
60
+ def to_s
61
+ return '' if @raw_output.nil?
62
+
63
+ format_output_string(@raw_output.to_s)
64
+ end
65
+
66
+ def inspect
67
+ "#<#{self.class}:#{object_id} exitcode=#{exitcode.inspect} stdout=#{stdout.inspect} stderr=#{stderr.inspect}>"
68
+ end
69
+
60
70
  private
61
71
 
62
72
  def format_output_string(str)
63
73
  # Cut the number of spaces in half, replace tabs with double spaces
64
74
  str.gsub!(/\s\s/, ' ')
65
75
  str.gsub!(/\t/, ' ')
66
- return "#{@line_prefix}#{str}" if str.length <= 115
76
+ return "#{@line_prefix}#{str}" if str.length <= 180
67
77
 
68
- str.scan(/.{1,115}/).map { |s| "#{@line_prefix}#{s}" }.join("\n")
78
+ str.scan(/.{1,180}/).map { |s| "#{@line_prefix}#{s}" }.instance_eval { |a|
79
+ [a.first] + a.drop(1).map { |s| " #{s}" }
80
+ }.join("\n")
69
81
  end
70
82
 
71
83
  def set_vars_from_output(out)
data/lib/cem_win_spec.rb CHANGED
@@ -8,16 +8,24 @@ module CemWinSpec
8
8
  include CemWinSpec::Logging
9
9
  end
10
10
 
11
+ def self.exit_code
12
+ @exit_code ||= 0
13
+ end
14
+
15
+ def self.exit_code=(value)
16
+ @exit_code = value
17
+ end
18
+
11
19
  def self.signal_handler(runner)
12
20
  Signal.trap('INT') do
13
- puts 'Caught interrupt, killing tunnel and exiting'
14
- runner.iap_tunnel.stop(wait: false, log: false)
21
+ puts 'Caught SIGINT, killing tunnel and exiting'
22
+ runner&.iap_tunnel&.stop(wait: false, log: false)
15
23
  exit 1
16
24
  end
17
25
  Signal.trap('TERM') do
18
- puts 'Caught interrupt, killing tunnel and exiting'
19
- runner.iap_tunnel.stop(wait: false, log: false)
20
- exit 1
26
+ puts 'Caught SIGTERM, killing tunnel and exiting'
27
+ runner&.iap_tunnel&.stop(wait: false, log: false)
28
+ exit!
21
29
  end
22
30
  end
23
31
 
@@ -38,25 +46,25 @@ module CemWinSpec
38
46
  logger.debug "Created TestRunner: #{runner}"
39
47
  signal_handler(runner)
40
48
  logger.debug 'Set up signal handler'
49
+ working_dir = nil
41
50
  begin
42
51
  sre_out = setup_remote_environment(runner)
43
- check_output!(sre_out, runner)
44
- module_dir = sre_out.stdout.chomp
52
+ working_dir = sre_out.stdout.chomp
53
+ logger.debug "Working dir: #{working_dir}"
54
+ upload_out = upload_module(runner, working_dir)
55
+ module_dir = upload_out.stdout.chomp
45
56
  logger.debug "Module dir: #{module_dir}"
46
- srr_out = setup_remote_ruby(runner, module_dir, **options)
47
- check_output!(srr_out, runner)
57
+ setup_remote_ruby(runner, module_dir, **options)
48
58
  case operation
49
59
  when :spec
50
- spec_out = run_spec(runner, module_dir, **options)
51
- check_output!(spec_out, runner)
60
+ run_spec(runner, module_dir, **options)
52
61
  when :clean_fixture_cache
53
- clean_fixture_cache_out = clean_fixture_cache(runner, module_dir, **options)
54
- check_output!(clean_fixture_cache_out, runner)
62
+ clean_fixture_cache(runner, module_dir, **options)
55
63
  else
56
64
  raise ArgumentError, "Unknown operation: #{operation}"
57
65
  end
58
66
  rescue StandardError => e
59
- if operation == :spec
67
+ if operation == :spec && runner.retry?
60
68
  begin
61
69
  logger.warn "Error running spec: #{e.message}"
62
70
  logger.debug e.backtrace.join("\n")
@@ -67,54 +75,98 @@ module CemWinSpec
67
75
  else
68
76
  handle_run_error(e)
69
77
  end
78
+ ensure
79
+ if working_dir
80
+ begin
81
+ logger.info "Cleaning up working directory #{working_dir}"
82
+ clean_up(runner, working_dir, **options)
83
+ logger.debug 'Finished cleaning up working directory'
84
+ rescue StandardError => e
85
+ handle_run_error(e)
86
+ end
87
+ end
88
+ runner&.iap_tunnel&.stop(log: false)
89
+ logger.info 'Finished running cem-win-spec'
70
90
  end
71
91
  end
72
92
 
73
93
  def self.handle_run_error(err)
74
94
  logger.fatal "Error: #{err.message}"
75
95
  logger.debug err.backtrace.join("\n")
76
- exit 1
96
+ self.exit_code = 1
77
97
  end
78
98
 
79
99
  def self.retry_spec_on_error(runner, module_dir, **options)
80
100
  logger.info 'Cleaning cache and trying again...'
81
- clean_fixture_cache_out = clean_fixture_cache(runner, module_dir, **options)
82
- check_output!(clean_fixture_cache_out, runner)
83
- spec_out = run_spec(runner, module_dir, **options)
84
- check_output!(spec_out, runner)
101
+ validate_output do
102
+ clean_fixture_cache(runner, module_dir, **options)
103
+ end
104
+ validate_output do
105
+ run_spec(runner, module_dir, **options)
106
+ end
85
107
  end
86
108
 
87
109
  def self.setup_remote_environment(runner)
88
- runner.enable_long_paths.run
89
- runner.enable_symlinks.run
90
- working_dir_out = runner.create_working_dir.run
91
- working_dir = working_dir_out.stdout.chomp
92
- logger.debug "Working dir: #{working_dir}"
93
- runner.upload_module(operation_timeout: 300, receive_timeout: 310, working_dir: working_dir).run
110
+ validate_output do
111
+ runner.enable_long_paths.run
112
+ end
113
+ validate_output do
114
+ runner.enable_symlinks.run
115
+ end
116
+ validate_output do
117
+ runner.create_working_dir.run
118
+ end
119
+ end
120
+
121
+ def self.upload_module(runner, working_dir)
122
+ validate_output do
123
+ runner.upload_module(operation_timeout: 300, receive_timeout: 310, working_dir: working_dir).run
124
+ end
94
125
  end
95
126
 
96
127
  def self.setup_remote_ruby(runner, module_dir, **opts)
97
- runner.setup_ruby(operation_timeout: 300,
98
- receive_timeout: 310,
99
- working_dir: module_dir,
100
- reuse_tunnel: false,
101
- **opts).run
128
+ validate_output do
129
+ runner.setup_ruby(operation_timeout: 300,
130
+ receive_timeout: 310,
131
+ working_dir: module_dir,
132
+ reuse_tunnel: false,
133
+ **opts).run
134
+ end
102
135
  end
103
136
 
104
137
  # Runs RSpec tests
105
138
  def self.run_spec(runner, module_dir, **opts)
106
- runner.rspec_tests_parallel(operation_timeout: 300,
107
- receive_timeout: 310,
108
- working_dir: module_dir,
109
- ignore_exitcode: true,
110
- reuse_tunnel: false,
111
- **opts).run
139
+ output = validate_output(validation_method: :inspect) do
140
+ runner.rspec_tests_parallel(operation_timeout: 300,
141
+ receive_timeout: 310,
142
+ working_dir: module_dir,
143
+ ignore_exitcode: true,
144
+ reuse_tunnel: false,
145
+ **opts).run
146
+ end
147
+ if output.stderr&.include?('Tests Failed')
148
+ logger.error 'Tests failed'
149
+ self.exit_code = 1
150
+ end
151
+ end
152
+
153
+ # Clean up the remote working directory
154
+ # @param options [Hash] Options for the test runner
155
+ def self.clean_up(runner, working_dir, **opts)
156
+ validate_output do
157
+ runner.clean_up(operation_timeout: 300,
158
+ receive_timeout: 310,
159
+ working_dir: working_dir,
160
+ **opts).run
161
+ end
112
162
  end
113
163
 
114
164
  # Clean the remote fixture cache
115
165
  # @param options [Hash] Options for the test runner
116
166
  def self.clean_fixture_cache(runner, module_dir, **opts)
117
- runner.clean_fixture_cache(working_dir: module_dir, **opts).run
167
+ validate_output do
168
+ runner.clean_fixture_cache(working_dir: module_dir, **opts).run
169
+ end
118
170
  end
119
171
 
120
172
  def self.check_output!(output, runner = nil)
@@ -123,4 +175,30 @@ module CemWinSpec
123
175
  raise "Command failed with exit code #{output.exitcode}"
124
176
  end
125
177
  end
178
+
179
+ class CemWinSpecValidationError < StandardError; end
180
+
181
+ def self.validate_output(*args, validation_method: :success?, no_output_strat: :debug, **kwargs)
182
+ raise ArgumentError, 'Block required' unless block_given?
183
+
184
+ output = yield *args, **kwargs
185
+ unless output
186
+ case no_output_strat
187
+ when :raise
188
+ raise 'No output'
189
+ when :warn
190
+ logger.warn 'No output'
191
+ when :debug
192
+ logger.debug 'No output'
193
+ else
194
+ # Do nothing
195
+ end
196
+ return false
197
+ end
198
+
199
+ unless output.send(validation_method)
200
+ raise CemWinSpecValidationError, "Output validation \"#{validation_method}\" failed:\n#{output}"
201
+ end
202
+ output
203
+ end
126
204
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cem_win_spec
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 0.1.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Heston Snodgrass
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-09-26 00:00:00.000000000 Z
11
+ date: 2025-02-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: winrm
@@ -139,7 +139,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
139
139
  - !ruby/object:Gem::Version
140
140
  version: '0'
141
141
  requirements: []
142
- rubygems_version: 3.4.18
142
+ rubygems_version: 3.5.18
143
143
  signing_key:
144
144
  specification_version: 4
145
145
  summary: Write a short summary, because RubyGems requires one.