jobmanager 1.0.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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