jobmanager 1.0.1 → 1.1.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.
@@ -20,7 +20,7 @@ require 'jobmanager'
20
20
 
21
21
  require 'test/unit'
22
22
  require_relative('helpers')
23
- require 'mock_syslog'
23
+ require_relative('mock_syslog')
24
24
 
25
25
  #
26
26
  # This class essentially tests the bin/jobmanager application since
@@ -60,7 +60,8 @@ class TestJobManager < Test::Unit::TestCase
60
60
  # echo command. Confirm the output logs produced (the job log file
61
61
  # and the central log file). Also, confirm that the job logs are
62
62
  # properly rotated.
63
- def test_jobmanager_1
63
+ def test_jobmanager_1_rotate_logs
64
+
64
65
  config_file = File.join(CONFIG_DIR, "jobmanager_1.yaml")
65
66
  config = File.open(config_file) { |fs| YAML::load(fs) }
66
67
 
@@ -89,7 +90,7 @@ class TestJobManager < Test::Unit::TestCase
89
90
  print jobmanager_log_contents, "\n"
90
91
 
91
92
  lines = jobmanager_log_contents.split("\n")
92
- assert_match(/^I, .* INFO .* \[#{USER_NAME}, echo\] Exited successfully$/, lines[1])
93
+ assert_match(/^I, .* INFO .* \[#{USER_NAME}, echo\] Job exited successfully$/, lines[1])
93
94
 
94
95
  lines.each { |line| assert_no_match(/^ERROR/, line) }
95
96
 
@@ -117,7 +118,8 @@ class TestJobManager < Test::Unit::TestCase
117
118
  # log central log information via syslog, which has been mocked out
118
119
  # for testing purposes. Confirm the email generated is correct, and
119
120
  # that the job log is properly rotated and zipped.
120
- def test_jobmanager_2
121
+ def test_jobmanager_2_syslog
122
+
121
123
  config_file = File.join(CONFIG_DIR, "jobmanager_2.yaml")
122
124
  command_line_args = [ "-j", "ze_echoer",
123
125
  "echo 1"
@@ -127,7 +129,8 @@ class TestJobManager < Test::Unit::TestCase
127
129
  # JobManager::Application class will also load the configuration
128
130
  # separately).
129
131
  yaml_reader = ConfigToolkit::YAMLReader.new(config_file)
130
- command_parameters_hash = JobManager::ApplicationOptionParser.parse(PROGRAM_NAME, command_line_args.dup())
132
+ command_parameters_hash = JobManager::ApplicationOptionParser.parse(PROGRAM_NAME,
133
+ command_line_args.dup())
131
134
  hash_reader = ConfigToolkit::HashReader.new(command_parameters_hash)
132
135
  override_reader = ConfigToolkit::OverrideReader.new(yaml_reader, hash_reader)
133
136
  config = JobManager::ApplicationConfig.load(override_reader)
@@ -168,7 +171,7 @@ class TestJobManager < Test::Unit::TestCase
168
171
  print jobmanager_log_contents, "\n"
169
172
 
170
173
  lines = jobmanager_log_contents.split("\n")
171
- assert_match(/INFO \[#{USER_NAME}, ze_echoer\] Exited successfully$/, lines[1])
174
+ assert_match(/INFO \[#{USER_NAME}, ze_echoer\] Job exited successfully$/, lines[1])
172
175
  lines.each { |line| assert_no_match(/ERROR/, line) }
173
176
 
174
177
  # confirm the correctness of the job log file.
@@ -194,7 +197,7 @@ class TestJobManager < Test::Unit::TestCase
194
197
 
195
198
  assert_equal("jobmanager results for job ze_echoer, command echo 1, user janet, host #{HOST_NAME}, result Success",
196
199
  delivery.subject)
197
- assert_match(/^INFO \[#{USER_NAME}, ze_echoer\] Exited successfully$/, delivery.body)
200
+ assert_match(/^INFO \[#{USER_NAME}, ze_echoer\] Job exited successfully$/, delivery.body)
198
201
  end
199
202
 
200
203
  # test config w/ email + date extensions, command that exists but fails
@@ -203,9 +206,9 @@ class TestJobManager < Test::Unit::TestCase
203
206
  # a bogus file, which will cause the command to fail. Confirm the
204
207
  # output logs produced (the job log file and the central log) are
205
208
  # correct. Confirm the email generated is correct.
206
- def test_jobmanager_3
209
+ def test_jobmanager_3_job_failure
210
+
207
211
  config_file = File.join(CONFIG_DIR, "jobmanager_3.yaml")
208
- config = File.open(config_file) { |fs| YAML::load(fs) }
209
212
 
210
213
  assert(JobManager::System.which("cat"))
211
214
  args = [ "cat #{TEMP_DIR}/no_file" ]
@@ -222,7 +225,7 @@ class TestJobManager < Test::Unit::TestCase
222
225
  print jobmanager_log_contents, "\n"
223
226
 
224
227
  lines = jobmanager_log_contents.split("\n")
225
- assert_match(/^ERROR \[janet, cat\] Failed! - exited normally with exit code:/, jobmanager_log_contents)
228
+ assert_match(/^ERROR \[janet, cat\] Failed! - Job exited normally with exit code:/, jobmanager_log_contents)
226
229
  lines.each { |line| assert_no_match(/Exited successfully/, line) }
227
230
 
228
231
  # confirm the email generated is correct.
@@ -232,35 +235,111 @@ class TestJobManager < Test::Unit::TestCase
232
235
  delivery = deliveries[0]
233
236
 
234
237
  assert_equal("jobmanager results for cat : Failure", delivery.subject)
235
- assert_match(/^ERROR \[#{USER_NAME}, cat\] Failed! - exited normally with exit code:/,
236
- delivery.body)
237
-
238
+ assert_match(/^ERROR \[#{USER_NAME}, cat\] Failed! - Job exited normally with exit code:/,
239
+ delivery.body)
238
240
  end
239
241
 
240
242
  #
241
- # Test the following error cases:
242
- # 1) The central log file can't be created/opened.
243
- # 2) The email results file can't be created/opened.
244
- # 3) The job logs directory can't be created.
245
- # 4) The job logs directory exists but is permissioned such that a file can't be created inside.
246
- #
243
+ # Test the following exception cases:
244
+ # 1) The central log file can't be created/opened (The job will not be started).
245
+ # 2) The email settings file can't be opened (The job will not be started).
246
+ #
247
247
  def test_jobmanager_exceptions()
248
248
  jobmanager_exception_bad_central_log_file()
249
249
  teardown(); setup();
250
250
 
251
251
  jobmanager_exception_bad_email_settings_file()
252
252
  teardown(); setup();
253
+ end
253
254
 
254
- jobmanager_exception_bad_job_logs_directory_1()
255
- teardown(); setup();
255
+ #
256
+ # In this test case, the job produces no output and should complete successfully.
257
+ # 1) Verify that no job log file will be created.
258
+ # 2) Verify that no email will be sent (email_condition = on_job_output_or_failure).
259
+ #
260
+ def test_jobmanager_6_no_job_output()
261
+
262
+ config_file = File.join(CONFIG_DIR, "jobmanager_6.yaml")
263
+
264
+ jobmanager = JobManager::Application.new(PROGRAM_NAME,
265
+ args = [ "ls > /dev/null" ],
266
+ config_file,
267
+ INTERACTIVE_SHELL_OVERRIDE)
268
+
269
+ jobmanager.run()
270
+
271
+ jobmanager_log_contents = File.read("temp/logs/jobmanager.log")
272
+
273
+ print "\nJob Manager Log Contents #6 (jobmanager_6.yaml):\n"
274
+ print jobmanager_log_contents, "\n"
275
+
276
+ lines = jobmanager_log_contents.split("\n")
277
+ assert_match(/^I, .* : \[#{USER_NAME}, ls\] Job exited successfully/,
278
+ lines[1])
279
+
280
+ # confirm that no job log files exist
281
+ assert_equal(0, Dir.glob("temp/logs/ls*").length)
282
+
283
+ # confirm that no email was generated (as email_condition == on_job_output_or_failure)
284
+ deliveries = ActionMailer::Base.deliveries
285
+ assert_equal(0, deliveries.length)
286
+ end
287
+
288
+ #
289
+ # In this test, the job should succeed and produce output. Test that
290
+ # 1) The job log is not rotated
291
+ # 2) No email is sent out (email_condition = on_job_output_or_failure, job_output is not empty)
292
+ #
293
+ def test_jobmanager_7_no_log_rotation()
294
+ config_file = File.join(CONFIG_DIR, "jobmanager_7_no_log_rotation.yaml")
295
+
296
+ jobmanager = JobManager::Application.new(PROGRAM_NAME,
297
+ args = [ "echo hello" ],
298
+ config_file,
299
+ INTERACTIVE_SHELL_OVERRIDE)
300
+
301
+ jobmanager.run()
302
+
303
+ jobmanager_log_contents = File.read("temp/logs/jobmanager.log")
304
+
305
+ print "\nJob Manager Log Contents #7 (jobmanager_7_no_log_rotation.yaml):\n"
306
+ print jobmanager_log_contents, "\n"
307
+
308
+ lines = jobmanager_log_contents.split("\n")
309
+ assert_match(/^I, .* : \[#{USER_NAME}, echo\] Job exited successfully/,
310
+ lines[1])
311
+
312
+ # confirm that the job log file was not rotated
313
+ job_log_files = Dir.glob("temp/logs/echo*")
314
+ assert_equal(1, job_log_files.length)
315
+ expected_job_log_file = "temp/logs/echo.log"
316
+ assert_equal(expected_job_log_file, job_log_files[0])
317
+
318
+ job_log_contents = File.read(expected_job_log_file)
319
+ print "\nJob Log Contents:\n"
320
+ print job_log_contents, "\n"
256
321
 
257
- jobmanager_exception_bad_job_logs_directory_2()
322
+ # confirm that the date was prepended to each line in the job log
323
+ job_log_contents.each do |line|
324
+ assert_match(/^\d{4}-\d{2}-\d{2} hello/, line)
325
+ end
326
+
327
+ # confirm that an email was generated (email_condition == on_job_output_or_failure)
328
+ deliveries = ActionMailer::Base.deliveries
329
+ assert_equal(1, deliveries.length)
258
330
 
331
+ # confirm the contents of the email
259
332
  end
333
+
260
334
 
261
- # In this test case, the job logs directory can't be created.
262
- # Test that the error is properly raised, logged, and emailed.
263
- def jobmanager_exception_bad_job_logs_directory_1()
335
+ #
336
+ # In this test case, the job logs directory can't be created. The job will still be
337
+ # started. Verify that
338
+ # 1) the job runs to completion, exits successfully
339
+ # 2) the error is properly logged to the central log file
340
+ # 3) the error is properly reported via email (email_condition == on_job_output_or_failure, job_output == empty string)
341
+ #
342
+ def test_jobmanager_4_bad_job_logs_directory()
264
343
 
265
344
  # Permission the directory so that the central log file can't be created inside this directory.
266
345
  FileUtils.mkdir_p("#{TEMP_DIR}/permissioned_directory")
@@ -268,14 +347,12 @@ class TestJobManager < Test::Unit::TestCase
268
347
 
269
348
  config_file = File.join(CONFIG_DIR, "jobmanager_4_bad_job_logs_directory_1.yaml")
270
349
 
271
- assert_raise_message("Error creating job_logs_directory", JobManager::Application::ComposedError) do
272
- jobmanager = JobManager::Application.new(PROGRAM_NAME,
273
- args = [ "ls" ],
274
- config_file,
275
- INTERACTIVE_SHELL_OVERRIDE)
350
+ jobmanager = JobManager::Application.new(PROGRAM_NAME,
351
+ args = [ "ls" ],
352
+ config_file,
353
+ INTERACTIVE_SHELL_OVERRIDE)
276
354
 
277
- jobmanager.run()
278
- end
355
+ jobmanager.run()
279
356
 
280
357
  # return the permissions
281
358
  FileUtils.chmod(0755, "#{TEMP_DIR}/permissioned_directory")
@@ -289,8 +366,8 @@ class TestJobManager < Test::Unit::TestCase
289
366
  print jobmanager_log_contents, "\n"
290
367
 
291
368
  lines = jobmanager_log_contents.split("\n")
292
- assert_match(/^E, .* : \[#{USER_NAME}, ls\] Error creating job_logs_directory: Error \(Errno::EACCES\): Permission denied - temp\/permissioned_directory\/job_logs_directory/,
293
- lines[0])
369
+ assert_match(/^E, .* : \[#{USER_NAME}, ls\] Error writing to job log: .* Failed to create directory temp\/permissioned_directory\/job_logs_directory/,
370
+ lines[1])
294
371
 
295
372
  # confirm the email generated is correct.
296
373
  deliveries = ActionMailer::Base.deliveries
@@ -299,30 +376,32 @@ class TestJobManager < Test::Unit::TestCase
299
376
  delivery = deliveries[0]
300
377
 
301
378
  assert_equal("jobmanager results for ls on #{HOST_NAME} : Failure", delivery.subject)
302
- assert_match(/^E, .* : \[#{USER_NAME}, ls\] Error creating job_logs_directory: Error \(Errno::EACCES\): Permission denied - temp\/permissioned_directory\/job_logs_directory/,
379
+
380
+ # confirm that the job completed successfully
381
+ assert_match(/: \[#{USER_NAME}, ls\] Job exited successfully/, delivery.body)
382
+
383
+ # confirm that the failure to write to the job log file was recorded in the email
384
+ assert_match(/E, .* : \[#{USER_NAME}, ls\] Error writing to job log: .* Failed to create directory temp\/permissioned_directory\/job_logs_directory/,
303
385
  delivery.body)
304
-
305
386
  end
306
387
 
307
-
308
388
  # In this test case, the job logs directory exists but permissions prevent a job log from being created.
309
389
  # Test that the error is properly raised, logged, and emailed.
310
- def jobmanager_exception_bad_job_logs_directory_2()
390
+ def test_jobmanager_5_bad_job_log_file()
311
391
 
312
392
  # Permission the directory so that the central log file can't be created inside this directory.
313
393
  FileUtils.mkdir_p("#{TEMP_DIR}/permissioned_directory")
314
394
  FileUtils.chmod(0000, "#{TEMP_DIR}/permissioned_directory")
315
395
 
316
- config_file = File.join(CONFIG_DIR, "jobmanager_4_bad_job_logs_directory_2.yaml")
396
+ config_file = File.join(CONFIG_DIR, "jobmanager_5_bad_job_log_file.yaml")
317
397
 
318
- assert_raise_message("Error opening job log file", JobManager::Application::ComposedError) do
319
- jobmanager = JobManager::Application.new(PROGRAM_NAME,
320
- args = [ "ls" ],
321
- config_file,
322
- INTERACTIVE_SHELL_OVERRIDE)
323
-
324
- jobmanager.run()
325
- end
398
+ args = [ "echo hello" ]
399
+ jobmanager = JobManager::Application.new(PROGRAM_NAME,
400
+ args,
401
+ config_file,
402
+ INTERACTIVE_SHELL_OVERRIDE)
403
+
404
+ jobmanager.run()
326
405
 
327
406
  # return the permissions
328
407
  FileUtils.chmod(0755, "#{TEMP_DIR}/permissioned_directory")
@@ -332,12 +411,12 @@ class TestJobManager < Test::Unit::TestCase
332
411
 
333
412
  jobmanager_log_contents = File.read("temp/logs/jobmanager.log")
334
413
 
335
- print "\nJob Manager Log Contents #4 (jobmanager_4_bad_job_logs_directory_2.yaml):\n"
414
+ print "\nJob Manager Log Contents #5 (jobmanager_5_bad_job_log_file.yaml):\n"
336
415
  print jobmanager_log_contents, "\n"
337
-
416
+
338
417
  lines = jobmanager_log_contents.split("\n")
339
- assert_match(/^E, .* : \[#{USER_NAME}, ls\] Error opening job log file: Error \(Errno::EACCES\): Permission denied - temp\/permissioned_directory\/ls\.log/,
340
- lines[0])
418
+ assert_match(/^E, .* : \[#{USER_NAME}, echo\] Error writing to job log: .* Failed to open file temp\/permissioned_directory\/echo.log/,
419
+ lines[1])
341
420
 
342
421
  # confirm the email generated is correct.
343
422
  deliveries = ActionMailer::Base.deliveries
@@ -345,10 +424,10 @@ class TestJobManager < Test::Unit::TestCase
345
424
 
346
425
  delivery = deliveries[0]
347
426
 
348
- assert_equal("jobmanager results for ls on #{HOST_NAME} : Failure", delivery.subject)
349
- assert_match(/^E, .* : \[#{USER_NAME}, ls\] Error opening job log file: Error \(Errno::EACCES\): Permission denied - temp\/permissioned_directory\/ls\.log/,
427
+ assert_equal("jobmanager results for echo on #{HOST_NAME} : Failure", delivery.subject)
428
+ assert_match(/^E, .* : \[#{USER_NAME}, echo\] Error writing to job log: .* Failed to open file temp\/permissioned_directory\/echo.log/,
350
429
  delivery.body)
351
-
430
+
352
431
  end
353
432
 
354
433
 
@@ -356,11 +435,6 @@ class TestJobManager < Test::Unit::TestCase
356
435
  # Test that the error is properly raised, logged, and not emailed.
357
436
  def jobmanager_exception_bad_email_settings_file()
358
437
 
359
- # Permission the directory so that the central log file can't be
360
- # created inside this directory.
361
- FileUtils.mkdir_p("#{TEMP_DIR}/permissioned_directory")
362
- FileUtils.chmod(0000, "#{TEMP_DIR}/permissioned_directory")
363
-
364
438
  config_file = File.join(CONFIG_DIR, "jobmanager_4_bad_email_settings_file.yaml")
365
439
 
366
440
  assert(JobManager::System.which("cat"))
@@ -374,9 +448,6 @@ class TestJobManager < Test::Unit::TestCase
374
448
  jobmanager.run()
375
449
  end
376
450
 
377
- # return the permissions
378
- FileUtils.chmod(0755, "#{TEMP_DIR}/permissioned_directory")
379
-
380
451
  # confirm correctness of syslog.
381
452
  jobmanager_log_contents = $test_syslog_logger.string()
382
453
 
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'jobmanager/openonfirstwritefile'
4
+
5
+ require 'rubygems'
6
+ require 'relative'
7
+ require 'assertions'
8
+
9
+ require 'test/unit'
10
+ require_relative('helpers')
11
+
12
+ class OpenOnFirstWriteFileTest < Test::Unit::TestCase
13
+ include TestHelpers
14
+
15
+ TEST_FILE = "#{TEMP_DIR}/test.dat"
16
+ def setup
17
+ teardown()
18
+ FileUtils.mkdir_p(TEMP_DIR)
19
+ end
20
+
21
+ def teardown
22
+ FileUtils::rm_r(TEMP_DIR, :force => true)
23
+ end
24
+
25
+ def test_success
26
+ # if the file stream isn't written to, the file should never be created
27
+ fs = JobManager::OpenOnFirstWriteFile.new(TEST_FILE, "w")
28
+ fs.flush()
29
+ fs.close()
30
+ assert_equal(false, File.file?(TEST_FILE))
31
+
32
+ # confirm that writing to the file works as expected
33
+ contents = "1.\n"
34
+ fs = JobManager::OpenOnFirstWriteFile.new(TEST_FILE, "w")
35
+ fs << contents
36
+ fs.close()
37
+ assert_equal(contents, File.read(TEST_FILE))
38
+ end
39
+
40
+ def test_failure
41
+ # permission a directory such that files and directories can not be created
42
+ # within it
43
+ permissioned_directory = "#{TEMP_DIR}/permissioned_directory"
44
+ FileUtils.mkdir_p(permissioned_directory)
45
+ FileUtils.chmod(0000, permissioned_directory)
46
+
47
+ # confirm that an exception is thrown if the file can not be created
48
+ test_file = File.join(permissioned_directory, "test.dat")
49
+ contents = "1.\n"
50
+ fs = JobManager::OpenOnFirstWriteFile.new(test_file, "w")
51
+
52
+ error_message = "Failed to open file temp/permissioned_directory/test.dat, Error (Errno::EACCES): Permission denied - temp/permissioned_directory/test.dat"
53
+ assert_raise_message(error_message, RuntimeError) do
54
+ fs << contents
55
+ end
56
+
57
+ # confirm that an exception is thrown if the file's containing directory can
58
+ # not be created.
59
+ test_file = "#{permissioned_directory}/sub_dir/test.dat"
60
+ contents = "1.\n"
61
+ fs = JobManager::OpenOnFirstWriteFile.new(test_file, "w")
62
+
63
+ error_message = "Failed to create directory temp/permissioned_directory/sub_dir, Error (Errno::EACCES): Permission denied - temp/permissioned_directory/sub_dir"
64
+ assert_raise_message(error_message, RuntimeError) do
65
+ fs << contents
66
+ end
67
+
68
+ # return the permissions
69
+ FileUtils.chmod(0755, "#{TEMP_DIR}/permissioned_directory")
70
+ end
71
+ end
@@ -98,7 +98,7 @@ class TestSystem < Test::Unit::TestCase
98
98
 
99
99
  assert_equal(false, success)
100
100
 
101
- assert_match(/ERROR \[#{USER_NAME}, catter\] Failed! - exited normally with exit code:/,
101
+ assert_match(/ERROR \[#{USER_NAME}, catter\] Failed! - Job exited normally with exit code:/,
102
102
  control_log_lines[-1])
103
103
  end
104
104
 
@@ -145,7 +145,7 @@ class TestSystem < Test::Unit::TestCase
145
145
  assert_equal(true, success)
146
146
  assert_equal(EXPECTED_SAMPLE_CONTENTS, log_stream.string())
147
147
 
148
- assert_match(/INFO \[#{USER_NAME}, catter\] Exited successfully/,
148
+ assert_match(/INFO \[#{USER_NAME}, catter\] Job exited successfully/,
149
149
  control_log_lines[-1])
150
150
  end
151
151
 
@@ -199,7 +199,7 @@ class TestSystem < Test::Unit::TestCase
199
199
 
200
200
  assert_equal(false, success)
201
201
 
202
- assert_match(/ERROR \[#{USER_NAME}, catter\] Failed! - exited abnormally due to signal:/,
202
+ assert_match(/ERROR \[#{USER_NAME}, catter\] Failed! - Job exited abnormally due to signal:/,
203
203
  control_log_lines[-1])
204
204
  end
205
205
  end
@@ -5,12 +5,23 @@ require 'fileutils'
5
5
 
6
6
  require 'rubygems'
7
7
  require 'relative'
8
+ require 'assertions'
8
9
 
9
10
  require 'jobmanager/teestream'
10
11
 
11
12
  require 'test/unit'
12
13
  require_relative('helpers')
13
14
 
15
+ class BogusStream
16
+ def initialize(error_message)
17
+ @error_message = error_message
18
+ end
19
+
20
+ def << (str)
21
+ raise @error_message
22
+ end
23
+ end
24
+
14
25
  #
15
26
  # This class tests the JobManager::TeeStream class.
16
27
  #
@@ -28,7 +39,7 @@ class TestTeeStream < Test::Unit::TestCase
28
39
  FileUtils::rm_r(TEMP_DIR, :force => true)
29
40
  end
30
41
 
31
- def test_case
42
+ def test_basic
32
43
  string_io = StringIO.new
33
44
 
34
45
  file_name = File.join(TEMP_DIR, "output.dat")
@@ -42,14 +53,46 @@ class TestTeeStream < Test::Unit::TestCase
42
53
  tee_stream.print "5\n"
43
54
 
44
55
  tee_stream.flush
56
+
57
+ str_string_io = tee_stream.stream(:string_io).string
45
58
  tee_stream.close
46
59
 
47
- str_string_io = string_io.string
48
60
  str_file = IO.read(file_name)
49
61
  expected = "1\n2\n3\n4\n5\n"
50
62
 
63
+ assert_equal(expected, string_io.string)
51
64
  assert_equal(expected, str_string_io)
52
65
  assert_equal(expected, str_file)
53
66
  end
67
+
68
+ def test_bad_stream
69
+ string_io = StringIO.new
70
+ bogus_stream_error = "Failed to Open Stream!"
71
+ bogus_stream = BogusStream.new(bogus_stream_error)
72
+
73
+ tee_stream = JobManager::TeeStream.new(:string_io => string_io,
74
+ :bogus_stream => bogus_stream)
75
+
76
+ 1.upto(5) do |iteration|
77
+
78
+ if (iteration == 1)
79
+ error_message = "Caught exception on stream bogus_stream, Error (RuntimeError): #{bogus_stream_error}"
80
+ assert_raise_message(error_message, RuntimeError) do
81
+ tee_stream << "#{iteration}\n"
82
+ end
83
+ else
84
+ tee_stream << "#{iteration}\n"
85
+ end
86
+
87
+ end
88
+ tee_stream.flush
89
+
90
+ str_string_io = tee_stream.stream(:string_io).string
91
+ tee_stream.close
92
+
93
+ expected = "1\n2\n3\n4\n5\n"
94
+ assert_equal(tee_stream.streams().length(), 1)
95
+ assert_equal(expected, str_string_io)
96
+ end
54
97
 
55
98
  end