beaker 1.12.2 → 1.13.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -7,7 +7,7 @@ module Unix::Pkg
7
7
  @apt_needs_update = true
8
8
  end
9
9
 
10
- def check_for_package(name)
10
+ def check_for_command(name)
11
11
  result = exec(Beaker::Command.new("which #{name}"), :acceptable_exit_codes => (0...127))
12
12
  case self['platform']
13
13
  when /solaris-10/
@@ -17,6 +17,27 @@ module Unix::Pkg
17
17
  end
18
18
  end
19
19
 
20
+ def check_for_package(name)
21
+ case self['platform']
22
+ when /sles-/
23
+ result = exec(Beaker::Command.new("zypper se -i --match-exact #{name}"), :acceptable_exit_codes => (0...127))
24
+ when /el-4/
25
+ @logger.debug("Package query not supported on rhel4")
26
+ return false
27
+ when /fedora|centos|el-/
28
+ result = exec(Beaker::Command.new("rpm -q #{name}"), :acceptable_exit_codes => (0...127))
29
+ when /ubuntu|debian/
30
+ result = exec(Beaker::Command.new("dpkg -s #{name}"), :acceptable_exit_codes => (0...127))
31
+ when /solaris-11/
32
+ result = exec(Beaker::Command.new("pkg info #{name}"), :acceptable_exit_codes => (0...127))
33
+ when /solaris-10/
34
+ result = exec(Beaker::Command.new("pkginfo #{name}"), :acceptable_exit_codes => (0...127))
35
+ else
36
+ raise "Package #{name} cannot be queried on #{self}"
37
+ end
38
+ result.exit_code == 0
39
+ end
40
+
20
41
  # If apt has not been updated since the last repo deployment it is
21
42
  # updated. Otherwise this is a noop
22
43
  def update_apt_if_needed
@@ -43,6 +43,7 @@ module Windows
43
43
  'hieralibdir' => '`cygpath -w /opt/puppet-git-repos/hiera/lib`',
44
44
  'hierapuppetlibdir' => '`cygpath -w /opt/puppet-git-repos/hiera-puppet/lib`',
45
45
  # PATH related variables need to be Unix, which cygwin converts
46
+ 'puppetbindir' => '$( [ -d "/cygdrive/c/Program Files (x86)" ] && echo "/cygdrive/c/Program Files (x86)" || echo "/cygdrive/c/Program Files" )/Puppet Labs/Puppet/bin',
46
47
  'hierabindir' => '/opt/puppet-git-repos/hiera/bin',
47
48
  'pathseparator' => ';',
48
49
  })
@@ -1,11 +1,16 @@
1
1
  module Windows::Pkg
2
2
  include Beaker::CommandFactory
3
3
 
4
- def check_for_package(name)
4
+ def check_for_command(name)
5
5
  result = exec(Beaker::Command.new("which #{name}"), :acceptable_exit_codes => (0...127))
6
6
  result.exit_code == 0
7
7
  end
8
8
 
9
+ def check_for_package(name)
10
+ result = exec(Beaker::Command.new("cygcheck #{name}"), :acceptable_exit_codes => (0...127))
11
+ result.exit_code == 0
12
+ end
13
+
9
14
  def install_package(name, cmdline_args = '')
10
15
  cygwin = ""
11
16
  rootdir = ""
@@ -20,7 +25,7 @@ module Windows::Pkg
20
25
  cygwin = "setup-x86.exe"
21
26
  end
22
27
 
23
- if not check_for_package(cygwin)
28
+ if not check_for_command(cygwin)
24
29
  execute("curl --retry 5 http://cygwin.com/#{cygwin} -o /cygdrive/c/Windows/System32/#{cygwin}")
25
30
  end
26
31
  execute("#{cygwin} -q -n -N -d -R #{cmdline_args} #{rootdir} -s http://cygwin.osuosl.org -P #{name}")
@@ -8,9 +8,9 @@ module Beaker
8
8
  NTPSERVER = 'pool.ntp.org'
9
9
  SLEEPWAIT = 5
10
10
  TRIES = 5
11
- PACKAGES = ['curl']
12
- UNIX_PACKAGES = ['ntpdate']
13
- SLES_PACKAGES = ['ntp']
11
+ UNIX_PACKAGES = ['curl', 'ntpdate']
12
+ WINDOWS_PACKAGES = ['curl']
13
+ SLES_PACKAGES = ['curl', 'ntp']
14
14
  ETC_HOSTS_PATH = "/etc/hosts"
15
15
  ETC_HOSTS_PATH_SOLARIS = "/etc/inet/hosts"
16
16
  ROOT_KEYS_SCRIPT = "https://raw.githubusercontent.com/puppetlabs/puppetlabs-sshkeys/master/templates/scripts/manage_root_authorized_keys"
@@ -67,9 +67,9 @@ module Beaker
67
67
  end
68
68
 
69
69
  #Validate that hosts are prepared to be used as SUTs, if packages are missing attempt to
70
- #install them. Verifies the presence of {HostPrebuiltSteps::PACKAGES} on all hosts,
71
- #{HostPrebuiltSteps::UNIX_PACKAGES} on unix platform hosts and {HostPrebuiltSteps::SLES_PACKAGES}
72
- #on sles (SUSE, Enterprise Linux) hosts.
70
+ #install them. Verifies the presence of #{HostPrebuiltSteps::UNIX_PACKAGES} on unix platform hosts,
71
+ #{HostPrebuiltSteps::SLES_PACKAGES} on SUSE platform hosts and {HostPrebuiltSteps::WINDOWS_PACKAGES} on windows
72
+ #platforms.
73
73
  # @param [Host, Array<Host>, String, Symbol] host One or more hosts to act upon
74
74
  # @param [Hash{Symbol=>String}] opts Options to alter execution.
75
75
  # @option opts [Beaker::Logger] :logger A {Beaker::Logger} object
@@ -78,24 +78,25 @@ module Beaker
78
78
  if host.is_a? Array
79
79
  host.map { |h| validate_host(h, opts) }
80
80
  else
81
- PACKAGES.each do |pkg|
82
- if not host.check_for_package pkg
83
- host.install_package pkg
84
- end
85
- end
86
81
  case
87
- when host['platform'] =~ /sles-/
88
- SLES_PACKAGES.each do |pkg|
89
- if not host.check_for_package pkg
90
- host.install_package pkg
91
- end
82
+ when host['platform'] =~ /sles-/
83
+ SLES_PACKAGES.each do |pkg|
84
+ if not host.check_for_package pkg
85
+ host.install_package pkg
86
+ end
87
+ end
88
+ when host['platform'] =~ /windows/
89
+ WINDOWS_PACKAGES.each do |pkg|
90
+ if not host.check_for_package pkg
91
+ host.install_package pkg
92
92
  end
93
- when host['platform'] !~ /(windows)|(aix)|(solaris)/
94
- UNIX_PACKAGES.each do |pkg|
95
- if not host.check_for_package pkg
96
- host.install_package pkg
97
- end
93
+ end
94
+ when host['platform'] !~ /aix|solaris|windows|sles-/
95
+ UNIX_PACKAGES.each do |pkg|
96
+ if not host.check_for_package pkg
97
+ host.install_package pkg
98
98
  end
99
+ end
99
100
  end
100
101
  end
101
102
  rescue => e
@@ -32,7 +32,7 @@ module Beaker
32
32
  })
33
33
 
34
34
  @logger.debug("Starting container #{container.id}")
35
- container.start({"PublishAllPorts" => true})
35
+ container.start({"PublishAllPorts" => true, "Privileged" => true})
36
36
 
37
37
  # Find out where the ssh port is from the container
38
38
  ip = container.json["NetworkSettings"]["Ports"]["22/tcp"][0]["HostIp"]
@@ -30,11 +30,24 @@ module Beaker
30
30
  v_file << " v.vm.box_url = '#{host['box_url']}'\n" unless host['box_url'].nil?
31
31
  v_file << " v.vm.base_mac = '#{randmac}'\n"
32
32
  v_file << " v.vm.network :private_network, ip: \"#{host['ip'].to_s}\", :netmask => \"#{host['netmask'] ||= "255.255.0.0"}\"\n"
33
+
34
+ if host['disk_path']
35
+ v_file << " v.vm.provider :virtualbox do |vb|\n"
36
+ v_file << " vb.name = '#{host.name}'\n"
37
+ unless File.exist?(host['disk_path'])
38
+ host['disk_path'] = File.join(host['disk_path'], "#{host.name}.vmdk")
39
+ v_file << " vb.customize ['createhd', '--filename', '#{host['disk_path']}', '--size', #{host['disk_size'] ||= 5 * 1024}, '--format', 'vmdk']\n"
40
+ end
41
+ v_file << " vb.customize ['storageattach', :id, '--storagectl', 'SATA Controller', '--port', 1, '--device', 0, '--type', 'hdd', '--medium','#{host['disk_path']}']\n"
42
+ v_file << " end\n"
43
+ end
44
+
33
45
  if /windows/i.match(host['platform'])
34
46
  v_file << " v.vm.network :forwarded_port, guest: 3389, host: 3389\n"
35
47
  v_file << " v.vm.network :forwarded_port, guest: 5985, host: 5985, id: 'winrm', auto_correct: true\n"
36
48
  v_file << " v.vm.guest = :windows"
37
49
  end
50
+
38
51
  v_file << " end\n"
39
52
  @logger.debug "created Vagrantfile for VagrantHost #{host.name}"
40
53
  end
@@ -0,0 +1,268 @@
1
+ <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
2
+ <xsl:output method="html" indent="yes"/>
3
+ <xsl:decimal-format decimal-separator="." grouping-separator="," />
4
+
5
+ <xsl:template match="testsuites">
6
+ <html>
7
+ <head>
8
+ <meta charset="utf-8"/>
9
+ <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
10
+ <meta name="viewport" content="width=device-width, initial-scale=1"/>
11
+ <link href="http://maxcdn.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet"/>
12
+ </head>
13
+
14
+ <body>
15
+ <!-- jQuery 2.1.1 -->
16
+ <script src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
17
+ <!-- Latest compiled and minified JavaScript -->
18
+ <script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
19
+
20
+
21
+ <xsl:variable name="time_format"><xsl:value-of select="'#0.00000'"/></xsl:variable>
22
+
23
+ <div class="container-fluid">
24
+ <div class="page-header">
25
+ <h1>Beaker <small>Puppet Labs Automated Acceptance Testing System</small></h1>
26
+ </div>
27
+
28
+ <!-- calculate overall stats for this run -->
29
+ <xsl:variable name="total_tests"><xsl:value-of select="sum(testsuite/@tests)"/></xsl:variable>
30
+ <xsl:variable name="total_errors"><xsl:value-of select="sum(testsuite/@errors)"/></xsl:variable>
31
+ <xsl:variable name="total_failures"><xsl:value-of select="sum(testsuite/@failures)"/></xsl:variable>
32
+ <xsl:variable name="total_time"><xsl:value-of select="sum(testsuite/@time)"/></xsl:variable>
33
+ <xsl:variable name="total_skip"><xsl:value-of select="sum(testsuite/@skip)"/></xsl:variable>
34
+ <xsl:variable name="total_pending"><xsl:value-of select="sum(testsuite/@pending)"/></xsl:variable>
35
+
36
+ <!-- determine if we overall passed or failed -->
37
+ <xsl:variable name="total_panel_type">
38
+ <xsl:choose>
39
+ <xsl:when test="$total_errors > 0 or $total_failures > 0">danger</xsl:when>
40
+ <xsl:otherwise>success</xsl:otherwise>
41
+ </xsl:choose>
42
+ </xsl:variable>
43
+ <!-- create the overall panel with stats, colored correctly based upon results -->
44
+ <div class="panel panel-{$total_panel_type}">
45
+ <div class="panel-heading">
46
+ <div class="panel-title">
47
+ <div class="row">
48
+ <div class="col-md-4">
49
+ <h2>Elapsed Time: <xsl:value-of select="format-number($total_time, $time_format)"/> sec </h2>
50
+ </div>
51
+ </div>
52
+ <div class="row">
53
+ <div class="col-md-4">
54
+ </div>
55
+ <div class="col-md-2">
56
+ <h2>Total: <xsl:value-of select="$total_tests" /> </h2>
57
+ </div>
58
+ <div class="col-md-2">
59
+ <h2>Failed: <xsl:value-of select="$total_errors + $total_failures" /></h2>
60
+ </div>
61
+ <div class="col-md-2">
62
+ <h2>Skipped: <xsl:value-of select="$total_skip" /> </h2>
63
+ </div>
64
+ <div class="col-md-2">
65
+ <h2>Pending: <xsl:value-of select="$total_pending" /> </h2>
66
+ </div>
67
+ </div> <!-- row -->
68
+ </div> <!-- panel-title -->
69
+ </div> <!--panel-heading -->
70
+
71
+ <div class="panel-body">
72
+ <div class="panel-group" id="accordion_one">
73
+ <xsl:for-each select="testsuite">
74
+ <!-- let's loop over the availables test suites -->
75
+ <xsl:variable name="testsuite_name"><xsl:value-of select="@name"/></xsl:variable>
76
+
77
+ <xsl:variable name="testsuite_name_safe" select="translate($testsuite_name,'.','_')" />
78
+ <xsl:variable name="testsuite_tests"><xsl:value-of select="@tests"/></xsl:variable>
79
+ <xsl:variable name="testsuite_errors"><xsl:value-of select="@errors"/></xsl:variable>
80
+ <xsl:variable name="testsuite_failures"><xsl:value-of select="@failures"/></xsl:variable>
81
+ <xsl:variable name="testsuite_time"><xsl:value-of select="@time"/></xsl:variable>
82
+ <xsl:variable name="testsuite_skip"><xsl:value-of select="@skip"/></xsl:variable>
83
+ <xsl:variable name="testsuite_pending"><xsl:value-of select="@pending"/></xsl:variable>
84
+ <xsl:variable name="testsuite_panel_type">
85
+ <xsl:choose>
86
+ <xsl:when test="$testsuite_errors > 0 or $testsuite_failures > 0">danger</xsl:when>
87
+ <xsl:otherwise>success</xsl:otherwise>
88
+ </xsl:choose>
89
+ </xsl:variable>
90
+ <div class="panel panel-{$testsuite_panel_type}">
91
+ <div class="panel-heading">
92
+ <div class="panel-title">
93
+ <a data-toggle="collapse" data-parent="#accordion_one" href="#{$testsuite_name_safe}">
94
+ <div class="row">
95
+ <div class="col-md-2">
96
+ <h4><xsl:value-of select="$testsuite_name" /></h4>
97
+ </div>
98
+ <div class="col-md-4">
99
+ <h4>Elapsed Time: <xsl:value-of select="format-number($testsuite_time, $time_format)"/> sec</h4>
100
+ </div>
101
+ </div>
102
+ <div class="row">
103
+ <div class="col-md-2">
104
+ </div>
105
+ <div class="col-md-2">
106
+ <h4>Total: <xsl:value-of select="$testsuite_tests" /></h4>
107
+ </div>
108
+ <div class="col-md-2">
109
+ <h4>Failed: <xsl:value-of select="$testsuite_errors + $testsuite_failures" /></h4>
110
+ </div>
111
+ <div class="col-md-2">
112
+ <h4>Skipped: <xsl:value-of select="$testsuite_skip" /></h4>
113
+ </div>
114
+ <div class="col-md-2">
115
+ <h4>Pending: <xsl:value-of select="$testsuite_pending" /></h4>
116
+ </div>
117
+ </div> <!-- row -->
118
+ </a>
119
+ </div> <!-- panel-title -->
120
+ </div> <!-- panel-heading -->
121
+ <div id="{$testsuite_name_safe}" class="panel-collapse collapse">
122
+ <div class="panel-body">
123
+ <div class="panel-group" id="accordion_two">
124
+ <div class="panel panel-primary">
125
+ <div class="panel-heading">
126
+ <h5 class="panel-title">
127
+ <a data-toggle="collapse" data-parent="#accordion_two" href="#{$testsuite_name_safe}-properties">
128
+ <div class="row">
129
+ <div class="col-md-4">
130
+ Properties
131
+ </div>
132
+ <div class="col-md-4">
133
+ </div>
134
+ </div> <!-- row -->
135
+ </a>
136
+ </h5> <!-- panel-title -->
137
+ </div> <!-- panel-heading -->
138
+ <div id="{$testsuite_name_safe}-properties" class="panel-collapse collapse in">
139
+ <div class="panel-body" style="height:425px">
140
+ <div class="panel panel-info" style="overflow-y: auto">
141
+ <div style="overflow-y:scroll; max-height:400px">
142
+ <!-- Default panel contents -->
143
+ <!-- Table -->
144
+ <table class="table">
145
+ <xsl:for-each select="properties/property">
146
+ <xsl:variable name="property_name"><xsl:value-of select="@name"/></xsl:variable>
147
+ <xsl:variable name="property_value"><xsl:value-of select="@value"/></xsl:variable>
148
+ <tr>
149
+ <td>
150
+ <xsl:value-of select="$property_name" />
151
+ </td>
152
+ <td>
153
+ <xsl:value-of select="$property_value" />
154
+ </td>
155
+ </tr>
156
+ </xsl:for-each>
157
+ </table>
158
+ </div> <!-- overflow div -->
159
+ </div> <!-- panel panel-info -->
160
+ </div> <!-- panel-body -->
161
+ </div> <!-- panel-collapse collapse -->
162
+ </div> <!-- panel panel-info -->
163
+ <!-- Start: Here are the test cases for a given test suite -->
164
+ <xsl:for-each select="testcase">
165
+ <xsl:variable name="testcase_name"><xsl:value-of select="@name"/></xsl:variable>
166
+ <xsl:variable name="testcase_classname"><xsl:value-of select="@classname"/></xsl:variable>
167
+ <xsl:variable name="testcase_fullpath"><xsl:value-of select="concat($testcase_classname, '/', $testcase_name)"/></xsl:variable>
168
+ <xsl:variable name="testcase_time"><xsl:value-of select="@time"/></xsl:variable>
169
+ <xsl:variable name="testcase_link" select="translate(translate($testcase_fullpath, '/', '_'), '.', '_')" />
170
+ <xsl:variable name="testcase_panel_type">
171
+ <xsl:choose>
172
+ <xsl:when test="failure or error">danger</xsl:when>
173
+ <xsl:when test="skip">warning</xsl:when>
174
+ <xsl:when test="pending">info</xsl:when>
175
+ <xsl:otherwise>success</xsl:otherwise>
176
+ </xsl:choose>
177
+ </xsl:variable>
178
+ <div class="panel panel-{$testcase_panel_type}">
179
+ <div class="panel-heading">
180
+ <div class="panel-title">
181
+ <a data-toggle="collapse" data-parent="#accordion_two" href="#{$testcase_link}">
182
+ <div class="row">
183
+ <div class="col-md-7">
184
+ <h5><xsl:value-of select="$testcase_name" /></h5>
185
+ </div>
186
+ </div>
187
+ <div class="row">
188
+ <div class="col-md-1">
189
+ </div>
190
+ <div class="col-md-7">
191
+ <h5>Path: <xsl:value-of select="$testcase_fullpath" /></h5>
192
+ </div>
193
+ <div class="col-md-4">
194
+ <h5>Elapsed Time: <xsl:value-of select="format-number($testcase_time, $time_format)"/> sec</h5>
195
+ </div>
196
+ </div> <!-- row -->
197
+ </a>
198
+ </div> <!-- panel-title -->
199
+ </div> <!-- panel-heading -->
200
+ <div id="{$testcase_link}" class="panel-collapse collapse in">
201
+ <div class="panel-body" style="height:425px">
202
+ <ul class="nav nav-tabs">
203
+ <li class="active"><a href="#tab1" data-toggle="tab">output</a></li>
204
+ <xsl:choose>
205
+ <xsl:when test="system-err and string(system-err)">
206
+ <li><a href="#tab2" data-toggle="tab">stderr</a></li>
207
+ </xsl:when>
208
+ <xsl:otherwise>
209
+ <li class="disabled"><a href="#tab2">stderr</a></li>
210
+ </xsl:otherwise>
211
+ </xsl:choose>
212
+ <xsl:choose>
213
+ <xsl:when test="failure">
214
+ <li><a href="#tab3" data-toggle="tab">failure</a></li>
215
+ </xsl:when>
216
+ <xsl:otherwise>
217
+ <li class="disabled"><a href="#tab3">failure</a></li>
218
+ </xsl:otherwise>
219
+ </xsl:choose>
220
+ </ul>
221
+ <div class="tab-content">
222
+ <div class="tab-pane active" id="tab1">
223
+ <pre class="pre-scrollable">
224
+ <xsl:value-of select="system-out" />
225
+ </pre>
226
+ </div>
227
+ <div class="tab-pane" id="tab2">
228
+ <pre class="pre-scrollable">
229
+ <xsl:value-of select="system-err" />
230
+ </pre>
231
+ </div>
232
+ <div class="tab-pane" id="tab3">
233
+ <div class="panel panel-default">
234
+ <div class="panel-heading"><xsl:value-of select="failure/@type" /></div>
235
+ <div class="panel-body">
236
+ <pre class="pre-scrollable">
237
+ <xsl:value-of select="failure/@message" />
238
+ </pre>
239
+ </div>
240
+ </div>
241
+ </div>
242
+ </div> <!-- tab-content -->
243
+ </div> <!-- panel-body -->
244
+ </div> <!-- panel-collapse collapse -->
245
+ </div> <!-- panel panel-default -->
246
+ </xsl:for-each>
247
+ </div> <!-- panel-group -->
248
+ <!-- Stop: Here are the test cases for a given test suite -->
249
+ </div> <!-- panel-body -->
250
+ </div> <!-- panel-collapse collapse -->
251
+ </div> <!-- panel panel-default -->
252
+ </xsl:for-each>
253
+ </div> <!-- panel-group -->
254
+
255
+ </div> <!-- panel-body -->
256
+ </div> <!-- panel panel -->
257
+ </div> <!-- container -->
258
+
259
+ <script type="text/javascript">
260
+ $('.collapse').collapse()
261
+ </script>
262
+ </body>
263
+
264
+ </html>
265
+
266
+
267
+ </xsl:template>
268
+ </xsl:stylesheet>
@@ -50,6 +50,7 @@ module Beaker
50
50
  def initialize(*args)
51
51
  options = args.last.is_a?(Hash) ? args.pop : {}
52
52
  @color = options[:color]
53
+ @sublog = nil
53
54
  case options[:log_level]
54
55
  when /debug/i, :debug
55
56
  @log_level = :debug
@@ -78,6 +79,8 @@ module Beaker
78
79
  case dest
79
80
  when IO
80
81
  @destinations << dest
82
+ when StringIO
83
+ @destinations << dest
81
84
  when String
82
85
  @destinations << File.open(dest, 'w')
83
86
  else
@@ -91,6 +94,8 @@ module Beaker
91
94
  case dest
92
95
  when IO
93
96
  @destinations.delete(dest)
97
+ when StringIO
98
+ @destinations.delete(dest)
94
99
  when String
95
100
  @destinations.delete_if {|d| d.respond_to?(:path) and d.path == dest}
96
101
  else
@@ -144,7 +149,7 @@ module Beaker
144
149
  # @param args[Array<String>] Strings to be reported
145
150
  def debug *args
146
151
  return unless is_verbose?
147
- optionally_color WHITE, args
152
+ optionally_color WHITE, *args
148
153
  end
149
154
 
150
155
  # Report a warning message.
@@ -162,14 +167,14 @@ module Beaker
162
167
  # @param args[Array<String>] Strings to be reported
163
168
  def info *args
164
169
  return unless is_info?
165
- optionally_color BLUE, args
170
+ optionally_color BLUE, *args
166
171
  end
167
172
 
168
173
  # Report a success message.
169
174
  # Will always be reported.
170
175
  # @param args[Array<String>] Strings to be reported
171
176
  def success *args
172
- optionally_color GREEN, args
177
+ optionally_color GREEN, *args
173
178
  end
174
179
 
175
180
  # Report a notify message.
@@ -177,14 +182,14 @@ module Beaker
177
182
  # @param args[Array<String>] Strings to be reported
178
183
  def notify *args
179
184
  return unless is_notify?
180
- optionally_color BRIGHT_WHITE, args
185
+ optionally_color BRIGHT_WHITE, *args
181
186
  end
182
187
 
183
188
  # Report an error message.
184
189
  # Will always be reported.
185
190
  # @param args[Array<String>] Strings to be reported
186
191
  def error *args
187
- optionally_color BRIGHT_RED, args
192
+ optionally_color BRIGHT_RED, *args
188
193
  end
189
194
 
190
195
  # Strip any color codes from provided string(s)
@@ -220,6 +225,21 @@ module Beaker
220
225
  expand_symlinks( trace ).join "\n"
221
226
  end
222
227
 
228
+ # Create a new StringIO log to track the current output
229
+ def start_sublog
230
+ if @sublog
231
+ remove_destination(@sublog)
232
+ end
233
+ @sublog = StringIO.new
234
+ add_destination(@sublog)
235
+ end
236
+
237
+ # Return the contents of the sublog
238
+ def get_sublog
239
+ @sublog.rewind
240
+ @sublog.read
241
+ end
242
+
223
243
  private
224
244
  # Expand each symlink found to its full path
225
245
  # Lines are assumed to be in the format "String : Integer"