cem_win_spec 0.1.5 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 155b49bb4366a1b2fca4237d9da909e05093d907a5e7ff326f4d7a9c4e75f375
4
- data.tar.gz: f374c915ab3afcf1636315c02b869fdd4690291070f8429418108d292a922cce
3
+ metadata.gz: 0f80bb9bd51e4ad8a8d005504e107a6c7761a4f8c63da2fe2df69f84e1afb1b4
4
+ data.tar.gz: 6eb10d5243db33711faa2b8f21914e05f3ce16cc6514ae6add15159f03c53266
5
5
  SHA512:
6
- metadata.gz: 5709e80190d88d38826deae83c379a948d5e7de72202891db48b00521801ef56e9718a64c681b17ccc7eb82a275af72c955737fef9ee2ab62a8c1fbdccb0aa51
7
- data.tar.gz: 3ec1282194e8a3afe8df90e3e13e7bd80428bf9d2a70f7d7b8fe6aea0437460bc3b35e8f7e19e8d3a55ded60076b489b078ba80b551b25aa917ccb9cdc0526d0
6
+ metadata.gz: 3203c1ce68eb0c392c3968b82cf80066023545b1a4522d415b1f199538bdfd7cef34c4b2e4c8bd154ddec5e95fe5fe1f5b88652aeac81f2650f8a1b27bf3a8f4
7
+ data.tar.gz: 8ef99fac173500c73992db6f059a704674f862bf17563a3f3aff05507eb01e715a829ea822bf6c1f3845bdae9640f90f8d178247c57ad28eb402250fc8046b48
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cem_win_spec (0.1.5)
4
+ cem_win_spec (0.1.7)
5
5
  parallel_tests (~> 3.4)
6
6
  puppet_forge (~> 4.1)
7
7
  winrm (~> 2.3)
@@ -11,17 +11,18 @@ GEM
11
11
  remote: https://rubygems.org/
12
12
  specs:
13
13
  ast (2.4.2)
14
+ bigdecimal (3.1.6)
14
15
  builder (3.2.4)
15
16
  coderay (1.1.3)
16
17
  diff-lcs (1.5.0)
17
18
  erubi (1.12.0)
18
- faraday (2.7.10)
19
- faraday-net_http (>= 2.0, < 3.1)
20
- ruby2_keywords (>= 0.0.4)
19
+ faraday (2.9.0)
20
+ faraday-net_http (>= 2.0, < 3.2)
21
21
  faraday-follow_redirects (0.3.0)
22
22
  faraday (>= 1, < 3)
23
- faraday-net_http (3.0.2)
24
- ffi (1.15.5)
23
+ faraday-net_http (3.1.0)
24
+ net-http
25
+ ffi (1.16.3)
25
26
  gssapi (1.3.1)
26
27
  ffi (>= 1.0.1)
27
28
  gyoku (1.4.0)
@@ -35,7 +36,10 @@ GEM
35
36
  method_source (1.0.0)
36
37
  minitar (0.9)
37
38
  multi_json (1.15.0)
38
- nori (2.6.0)
39
+ net-http (0.4.1)
40
+ uri
41
+ nori (2.7.0)
42
+ bigdecimal
39
43
  parallel (1.22.1)
40
44
  parallel_tests (3.13.0)
41
45
  parallel
@@ -78,11 +82,11 @@ GEM
78
82
  rubocop-ast (1.24.1)
79
83
  parser (>= 3.1.1.0)
80
84
  ruby-progressbar (1.11.0)
81
- ruby2_keywords (0.0.5)
82
85
  rubyntlm (0.6.3)
83
86
  rubyzip (2.3.2)
84
87
  semantic_puppet (1.1.0)
85
88
  unicode-display_width (2.1.0)
89
+ uri (0.13.0)
86
90
  winrm (2.3.6)
87
91
  builder (>= 2.1.2)
88
92
  erubi (~> 1.8)
@@ -8,6 +8,8 @@ require 'puppet_forge'
8
8
  require 'yaml'
9
9
 
10
10
  module CemWinSpec
11
+ class FixtureCacheError < StandardError; end
12
+
11
13
  # Class for managing cached fixtures
12
14
  # Fixture caching is used to speed up test runs by reusing the same
13
15
  # Puppet modules between test runs instead of downloading them each time.
@@ -24,11 +26,10 @@ module CemWinSpec
24
26
  attr_reader :cache_dir, :cache_entries
25
27
 
26
28
  def initialize
27
- raise 'FixtureCache must be ran on Windows' unless Gem.win_platform?
28
-
29
+ validate_platform!
29
30
  @cache_dir = CACHE_DIR
30
31
  @cache_entries = setup_and_load_cache
31
- @entries_digest = Digest::SHA256.hexdigest(@cache_entries.to_s)
32
+ @entries_digest = hexdigest(@cache_entries.to_s)
32
33
  @dependencies = dependencies_from_metadata
33
34
  setup!
34
35
  end
@@ -43,6 +44,8 @@ module CemWinSpec
43
44
  puts "Downloading #{name} #{data[:version]}..."
44
45
  download_and_cache(name, data[:release_slug], data[:checksum])
45
46
  end
47
+ rescue StandardError => e
48
+ handle_error(e, 'Failed to setup fixture cache')
46
49
  ensure
47
50
  save_cache_entries
48
51
  end
@@ -52,16 +55,37 @@ module CemWinSpec
52
55
  raise "Directory #{fixtures_dir} does not exist" unless File.directory?(fixtures_dir)
53
56
 
54
57
  @cache_entries.each do |_, path|
55
- ::FileUtils.cp_r(path, fixtures_dir)
56
- interim_mod_path = File.join(fixtures_dir, File.basename(path))
57
58
  final_mod_path = File.join(fixtures_dir, File.basename(path).split('-').last)
58
- ::FileUtils.mv(interim_mod_path, final_mod_path)
59
- puts "Copied #{File.basename(path)} to #{final_mod_path}"
59
+ ::FileUtils.remove_entry_secure(final_mod_path) if File.exist?(final_mod_path)
60
+ ::FileUtils.cp_r(path, final_mod_path)
61
+ puts "Copied #{path} to #{final_mod_path}"
60
62
  end
63
+ rescue StandardError => e
64
+ handle_error(e, "Failed to copy fixtures to #{fixtures_dir}")
61
65
  end
62
66
 
63
67
  private
64
68
 
69
+ def validate_platform!
70
+ return if Gem.win_platform?
71
+
72
+ $stderr.puts 'ERROR: FixtureCache must run on Windows'
73
+ raise FixtureCacheError, 'FixtureCache must run on Windows'
74
+ end
75
+
76
+ def hexdigest(str)
77
+ Digest(:SHA256).hexdigest(str)
78
+ end
79
+
80
+ def handle_error(e, msg = 'Fixture cache error')
81
+ $stderr.puts "ERROR: #{msg}: #{e}"
82
+ raise e if e.is_a?(FixtureCacheError)
83
+
84
+ new_err = FixtureCacheError.new("#{msg}: #{e}")
85
+ new_err.set_backtrace(e.backtrace)
86
+ raise new_err
87
+ end
88
+
65
89
  def dependencies_from_metadata
66
90
  raise "File metadata.json not found in current directory #{Dir.pwd}" unless File.exist?('metadata.json')
67
91
 
@@ -81,6 +105,8 @@ module CemWinSpec
81
105
  release_slug: latest_valid_release.slug,
82
106
  }
83
107
  end
108
+ rescue StandardError => e
109
+ handle_error(e, 'Failed to get dependencies from metadata.json')
84
110
  end
85
111
 
86
112
  def dep_version_req_satisfied?(version_req, version)
@@ -88,6 +114,8 @@ module CemWinSpec
88
114
  raise "Invalid version requirement #{version_req}" unless reqs.to_a.length > 1
89
115
 
90
116
  reqs.to_a[1..-1].all? { |req| version_req_satisfied?(req, version) }
117
+ rescue StandardError => e
118
+ handle_error(e, 'Failed checking version requirement satisfaction')
91
119
  end
92
120
 
93
121
  def version_req_satisfied?(version_req, version)
@@ -95,7 +123,7 @@ module CemWinSpec
95
123
  end
96
124
 
97
125
  def module_checksum(name, version_req, version)
98
- Digest::SHA256.hexdigest("#{name}#{version_req}#{version}")
126
+ hexdigest("#{name}#{version_req}#{version}")
99
127
  end
100
128
 
101
129
  def manifest
@@ -114,6 +142,8 @@ module CemWinSpec
114
142
  end
115
143
  puts "Loading cache manifest #{manifest}..."
116
144
  YAML.load_file(manifest)
145
+ rescue StandardError => e
146
+ handle_error(e, 'Failed to setup and load cache')
117
147
  end
118
148
 
119
149
  def cached?(checksum)
@@ -132,13 +162,17 @@ module CemWinSpec
132
162
  File.join(module_cache_dir, 'temp'))
133
163
  cache_entries[checksum] = File.join(module_cache_dir, name)
134
164
  puts "Downloaded #{release_slug} to #{File.join(module_cache_dir, name)}"
165
+ rescue StandardError => e
166
+ handle_error(e, "Failed to download and cache #{name} with slug #{release_slug} and checksum #{checksum}")
135
167
  end
136
168
 
137
169
  def save_cache_entries
138
- return if @entries_digest == Digest::SHA256.hexdigest(cache_entries.to_s)
170
+ return if @entries_digest == hexdigest(cache_entries.to_s)
139
171
 
140
172
  File.write(manifest, cache_entries.to_yaml)
141
173
  puts "Saved cache manifest #{manifest}"
174
+ rescue StandardError => e
175
+ handle_error(e, "Failed to save cache manifest #{manifest}")
142
176
  end
143
177
  end
144
178
  end
@@ -8,50 +8,37 @@ module CemWinSpec
8
8
  class ModuleArchiveBuilder
9
9
  include CemWinSpec::Logging
10
10
 
11
- FILE_ALLOWLIST = %w[
12
- data
13
- files
14
- lib
15
- manifests
16
- plans
17
- spec
18
- tasks
19
- types
20
- .fixtures.yml
21
- .gitignore
22
- .rspec
23
- .rubocop.yml
24
- .yardopts
25
- hiera.yaml
26
- Gemfile
27
- metadata.json
28
- Rakefile
11
+ FILE_EXCLUDELIST = %w[
12
+ .git/**/*
13
+ data/benchmarks/**/*
14
+ dev/**/*
15
+ spec/fixtures/modules/*
16
+ pkg/**/*
17
+ Gemfile.lock
29
18
  ].freeze
30
19
 
31
- attr_reader :module_dir, :allowlist, :tempdir, :archive
20
+ attr_reader :module_dir, :excludelist, :tempdir, :archive
32
21
 
33
- def initialize(module_dir = Dir.pwd, allowlist: FILE_ALLOWLIST)
22
+ def initialize(module_dir = Dir.pwd, excludelist: FILE_EXCLUDELIST)
34
23
  @module_dir = module_dir
35
24
  @module_name = File.basename(module_dir)
36
- @allowlist = allowlist
25
+ @excludelist = excludelist
37
26
  end
38
27
 
39
28
  alias path archive
40
29
 
41
- def build
30
+ def build(remove_when_complete: true)
42
31
  create_tempdir
43
32
  copy_module_to_tempdir
44
33
  remove_unwanted_files
45
34
  create_archive
46
35
  if block_given?
47
36
  begin
48
- yield archive
49
- ensure
50
- FileUtils.rm_rf(archive) if archive && File.exist?(archive)
37
+ yield File.join(tempdir, archive)
51
38
  end
52
39
  end
53
40
  ensure
54
- FileUtils.rm_rf(tempdir) if tempdir && File.exist?(tempdir)
41
+ FileUtils.rm_rf(tempdir) if remove_when_complete && tempdir && File.exist?(tempdir)
55
42
  end
56
43
 
57
44
  private
@@ -62,22 +49,30 @@ module CemWinSpec
62
49
  end
63
50
 
64
51
  def copy_module_to_tempdir
65
- FileUtils.cp_r(module_dir, tempdir)
52
+ FileUtils.cp_r(module_dir, tempdir, preserve: false)
66
53
  logger.debug "Copied #{module_dir} to #{tempdir}"
67
54
  @temp_module_dir = File.join(tempdir, @module_name)
68
55
  end
69
56
 
70
- def remove_unwanted_files
71
- to_remove = Dir.glob("*", File::FNM_DOTMATCH, base: @temp_module_dir).reject { |f| allowlist.include?(f) || %w[. ..].include?(f) }
72
- to_remove.each do |file|
73
- FileUtils.rm_rf(File.join(@temp_module_dir, file))
74
- logger.debug "Removed #{file} from #{@temp_module_dir}"
57
+ def unwanted_files
58
+ unwanted = []
59
+ Dir.glob('**/*', File::FNM_DOTMATCH, base: @temp_module_dir) do |f|
60
+ next if %w[. ..].include?(f)
61
+
62
+ unwanted << File.join(@temp_module_dir, f) if excludelist.any? { |e| File.fnmatch?(e, f, File::FNM_DOTMATCH) }
75
63
  end
64
+ unwanted
65
+ end
66
+
67
+ def remove_unwanted_files
68
+ FileUtils.rm_rf(unwanted_files, verbose: (logger.level.first <= Logger::DEBUG))
76
69
  end
77
70
 
78
71
  def create_archive
79
72
  @archive = "#{@module_name}.tar.gz"
80
- `tar -czf #{archive} -C #{tempdir} #{@module_name}`
73
+ Dir.chdir(tempdir) do
74
+ `COPYFILE_DISABLE=1 tar -czf #{archive} --no-acls --no-xattrs #{@module_name}`
75
+ end
81
76
  logger.info "Created module archive #{archive}"
82
77
  end
83
78
  end
@@ -81,7 +81,7 @@ namespace 'cem_win_spec' do
81
81
  task :prep do
82
82
  puts 'Preparing spec data...'
83
83
  data_fix = cem_win_spec_base_path_in_fixtures(cem_win_spec_data_dir)
84
- if File.directory?(data_fix)
84
+ if Dir.exist?(data_fix)
85
85
  puts "Removing old #{data_fix} directory..."
86
86
  ::FileUtils.remove_entry_secure(data_fix)
87
87
  end
@@ -91,16 +91,23 @@ namespace 'cem_win_spec' do
91
91
  raise 'Spec data copy failed!'
92
92
  end
93
93
  hiera_fix = cem_win_spec_base_path_in_fixtures(cem_win_spec_hiera_conf)
94
+ if File.file?(hiera_fix)
95
+ puts "Removing old #{hiera_fix} file..."
96
+ ::FileUtils.remove_entry_secure(hiera_fix)
97
+ end
94
98
  puts "Copying Hiera config to #{hiera_fix}..."
95
99
  ::FileUtils.cp(cem_win_spec_hiera_conf, hiera_fix)
96
- unless File.exist?(hiera_fix)
97
- raise 'Hiera config copy failed!'
98
- end
100
+ raise 'Hiera config copy failed!' unless File.exist?(hiera_fix)
101
+
99
102
  puts 'Spec data prepared successfully'
100
- puts 'Preparing module fixtures...'
101
- mod_fix = cem_win_spec_base_path_in_fixtures('modules')
103
+ puts 'Setting up module fixture cache...'
102
104
  cache = CemWinSpec::FixtureCache.new
105
+ puts 'Copying module fixtures to spec fixtures directory...'
106
+ mod_fix = cem_win_spec_base_path_in_fixtures('modules')
103
107
  cache.copy_fixtures_to(mod_fix)
108
+ puts 'Removing old module symlink...'
109
+ link_path = File.join(mod_fix, 'cem_windows')
110
+ ::FileUtils.remove_entry_secure(link_path) if File.exist?(link_path)
104
111
  puts 'Module fixtures prepared successfully'
105
112
  end
106
113
 
@@ -108,9 +115,10 @@ namespace 'cem_win_spec' do
108
115
  task :parallel_spec_standalone do
109
116
  spec_files = Rake::FileList[cem_win_spec_pattern]
110
117
  pargs = ['--type', 'rspec']
111
- pargs << '--trace' if cem_win_spec_trace
118
+ pargs << '--verbose' if cem_win_spec_trace
112
119
  rspec_args = ['--']
113
120
  rspec_args << '--fail-fast' if cem_win_spec_fail_fast
121
+ rspec_args << '--backtrace' if cem_win_spec_trace
114
122
  rspec_args.concat(['--format', cem_win_spec_format])
115
123
  pargs.concat(rspec_args)
116
124
  pargs << '--'
@@ -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)
@@ -68,7 +60,7 @@ module CemWinSpec
68
60
  module_archive_build { |a| remote_upload(a, remote_working_dir) }
69
61
  module_dir = "#{remote_working_dir}\\#{File.basename(module_archive_path, '.tar.gz')}"
70
62
  logger.debug "Module uploaded to #{module_dir}.tar.gz, extracting..."
71
- remote_run("tar -xzf #{module_dir}.tar.gz -C #{remote_working_dir}")
63
+ remote_run("tar -C #{remote_working_dir} -xzf #{module_dir}.tar.gz")
72
64
  logger.debug "Module extracted to #{module_dir}"
73
65
  module_dir
74
66
  end
@@ -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,10 +98,10 @@ module CemWinSpec
104
98
  end
105
99
  end
106
100
 
107
- def clean_up
101
+ def clean_up(working_dir)
108
102
  @clean_up ||= new_command('Cleanup') do |working_dir|
109
103
  if remote_available?
110
- remote_run(cleanup_cmd, quiet: true)
104
+ remote_run(cleanup_cmd, working_dir, quiet: true)
111
105
  else
112
106
  logger.warn 'Cleanup not available'
113
107
  end
@@ -120,6 +114,10 @@ module CemWinSpec
120
114
  end
121
115
  end
122
116
 
117
+ def inspect
118
+ "#<#{self.class.name}:0x#{object_id.to_s(16)}>"
119
+ end
120
+
123
121
  private
124
122
 
125
123
  def title_sym(title)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CemWinSpec
4
- VERSION = "0.1.5"
4
+ VERSION = "0.1.7"
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
 
@@ -80,12 +84,17 @@ module CemWinSpec
80
84
  logger.debug "Executing command:\n#{cmd.split(%r{\n|\r\n|;\s*}).map { |c| " #> #{c}" }.join("\n")}"
81
85
  end
82
86
 
87
+ # Commands that, when ran, will change the ruby version. This will also set the local bundle config path
83
88
  def change_ruby_version_cmd
84
- "uru #{ruby_version}"
89
+ [
90
+ "uru #{ruby_version}",
91
+ '$env:GEM_HOME = "$(ruby -e "puts Gem.dir")"',
92
+ 'bundle config set --local path "$env:GEM_HOME"',
93
+ ].join(COMMAND_SEPARATOR)
85
94
  end
86
95
 
87
96
  def set_env_var_cmd(key, value)
88
- "set #{key}=\"#{value}\""
97
+ "$env:#{key} = \"#{value}\""
89
98
  end
90
99
 
91
100
  def change_working_dir_cmd(dir)
@@ -29,8 +29,8 @@ module CemWinSpec
29
29
  begin
30
30
  shell = conn.shell(:powershell)
31
31
  output = shell.run(command(cmd)) do |stdout, stderr|
32
- logger << stdout unless stdout.nil? || stdout.empty?
33
- logger << stderr unless stderr.nil? || stderr.empty?
32
+ logger << stdout if stdout
33
+ logger << stderr if stderr
34
34
  end
35
35
  rescue WinRM::WinRMAuthorizationError => e
36
36
  @available = false
@@ -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
 
@@ -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
@@ -40,23 +40,22 @@ module CemWinSpec
40
40
  logger.debug 'Set up signal handler'
41
41
  begin
42
42
  sre_out = setup_remote_environment(runner)
43
- check_output!(sre_out, runner)
44
- module_dir = sre_out.stdout.chomp
43
+ working_dir = sre_out.stdout.chomp
44
+ logger.debug "Working dir: #{working_dir}"
45
+ upload_out = upload_module(runner, working_dir)
46
+ module_dir = upload_out.stdout.chomp
45
47
  logger.debug "Module dir: #{module_dir}"
46
- srr_out = setup_remote_ruby(runner, module_dir, **options)
47
- check_output!(srr_out, runner)
48
+ setup_remote_ruby(runner, module_dir, **options)
48
49
  case operation
49
50
  when :spec
50
- spec_out = run_spec(runner, module_dir, **options)
51
- check_output!(spec_out, runner)
51
+ run_spec(runner, module_dir, **options)
52
52
  when :clean_fixture_cache
53
- clean_fixture_cache_out = clean_fixture_cache(runner, module_dir, **options)
54
- check_output!(clean_fixture_cache_out, runner)
53
+ clean_fixture_cache(runner, module_dir, **options)
55
54
  else
56
55
  raise ArgumentError, "Unknown operation: #{operation}"
57
56
  end
58
57
  rescue StandardError => e
59
- if operation == :spec
58
+ if operation == :spec && runner.retry?
60
59
  begin
61
60
  logger.warn "Error running spec: #{e.message}"
62
61
  logger.debug e.backtrace.join("\n")
@@ -67,6 +66,9 @@ module CemWinSpec
67
66
  else
68
67
  handle_run_error(e)
69
68
  end
69
+ ensure
70
+ runner&.clean_up(working_dir) if working_dir
71
+ runner&.iap_tunnel&.stop(wait: false, log: false)
70
72
  end
71
73
  end
72
74
 
@@ -78,44 +80,60 @@ module CemWinSpec
78
80
 
79
81
  def self.retry_spec_on_error(runner, module_dir, **options)
80
82
  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)
83
+ validate_output do
84
+ clean_fixture_cache(runner, module_dir, **options)
85
+ end
86
+ validate_output do
87
+ run_spec(runner, module_dir, **options)
88
+ end
85
89
  end
86
90
 
87
91
  def self.setup_remote_environment(runner)
88
- runner.enable_long_paths.run
89
- runner.enable_symlinks.run
90
- runner.enable_symlinks.run
91
- working_dir_out = runner.create_working_dir.run
92
- working_dir = working_dir_out.stdout.chomp
93
- logger.debug "Working dir: #{working_dir}"
94
- runner.upload_module(operation_timeout: 300, receive_timeout: 310, working_dir: working_dir).run
92
+ validate_output do
93
+ runner.enable_long_paths.run
94
+ end
95
+ validate_output do
96
+ runner.enable_symlinks.run
97
+ end
98
+ validate_output do
99
+ runner.create_working_dir.run
100
+ end
101
+ end
102
+
103
+ def self.upload_module(runner, working_dir)
104
+ validate_output do
105
+ runner.upload_module(operation_timeout: 300, receive_timeout: 310, working_dir: working_dir).run
106
+ end
95
107
  end
96
108
 
97
109
  def self.setup_remote_ruby(runner, module_dir, **opts)
98
- runner.setup_ruby(operation_timeout: 300,
99
- receive_timeout: 310,
100
- working_dir: module_dir,
101
- reuse_tunnel: false,
102
- **opts).run
110
+ validate_output do
111
+ runner.setup_ruby(operation_timeout: 300,
112
+ receive_timeout: 310,
113
+ working_dir: module_dir,
114
+ reuse_tunnel: false,
115
+ **opts).run
116
+ end
103
117
  end
104
118
 
105
119
  # Runs RSpec tests
106
120
  def self.run_spec(runner, module_dir, **opts)
107
- runner.rspec_tests_parallel(operation_timeout: 300,
108
- receive_timeout: 310,
109
- working_dir: module_dir,
110
- ignore_exitcode: true,
111
- reuse_tunnel: false,
112
- **opts).run
121
+ output = validate_output(validation_method: :inspect) do
122
+ runner.rspec_tests_parallel(operation_timeout: 300,
123
+ receive_timeout: 310,
124
+ working_dir: module_dir,
125
+ ignore_exitcode: true,
126
+ reuse_tunnel: false,
127
+ **opts).run
128
+ end
113
129
  end
114
130
 
115
131
  # Clean the remote fixture cache
116
132
  # @param options [Hash] Options for the test runner
117
133
  def self.clean_fixture_cache(runner, module_dir, **opts)
118
- runner.clean_fixture_cache(working_dir: module_dir, **opts).run
134
+ validate_output do
135
+ runner.clean_fixture_cache(working_dir: module_dir, **opts).run
136
+ end
119
137
  end
120
138
 
121
139
  def self.check_output!(output, runner = nil)
@@ -124,4 +142,30 @@ module CemWinSpec
124
142
  raise "Command failed with exit code #{output.exitcode}"
125
143
  end
126
144
  end
145
+
146
+ class CemWinSpecValidationError < StandardError; end
147
+
148
+ def self.validate_output(*args, validation_method: :success?, no_output_strat: :debug, **kwargs)
149
+ raise ArgumentError, 'Block required' unless block_given?
150
+
151
+ output = yield *args, **kwargs
152
+ unless output
153
+ case no_output_strat
154
+ when :raise
155
+ raise 'No output'
156
+ when :warn
157
+ logger.warn 'No output'
158
+ when :debug
159
+ logger.debug 'No output'
160
+ else
161
+ # Do nothing
162
+ end
163
+ return false
164
+ end
165
+
166
+ unless output.send(validation_method)
167
+ raise CemWinSpecValidationError, "Output validation \"#{validation_method}\" failed: #{output}"
168
+ end
169
+ output
170
+ end
127
171
  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.5
4
+ version: 0.1.7
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-08-21 00:00:00.000000000 Z
11
+ date: 2024-03-29 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.4.22
143
143
  signing_key:
144
144
  specification_version: 4
145
145
  summary: Write a short summary, because RubyGems requires one.