beaker 1.12.2 → 1.13.0

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.
@@ -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"