yamatanooroti 0.0.1 → 0.0.6

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: 3f7c72bc6c902dd8b5cdf5d02183954cba1180960c702e5642c208b074e2ccd5
4
- data.tar.gz: d5dfea77ef4a055e0e278fa7447d96e1c473ce7aa4108062c99e3fc1d3e3d499
3
+ metadata.gz: 7ec6554c150a5c256de88a3feda8e9a3b073bfdfc1af5925e82b78ec29563c83
4
+ data.tar.gz: 54df3900576ae774f4c86642c98ae22854e8917927cbb570666b38ac22b8ba16
5
5
  SHA512:
6
- metadata.gz: d4242ac34c419417a1e8035968b26f4587d6d5c332852a73dad9d413b0b06447b1cd0969272b344bd3bc4a26fc4934e2a156cfc740d44fe53344f4d1f130048a
7
- data.tar.gz: 1f2679e02d8183d15c5ded5baa273d6ca1b4b299335145a4141066c3f315a0277623ca64caa1e0f5bdffe205280ce2b9b27cfe7b1f728c71e03f8293d7211b1c
6
+ metadata.gz: e5765147b02b72bb30969075614e335d8019b6dd8c6ae5535d5170e5c18533dd6275fa065f413a9e1a2d5d126e6679010670d0a72d9c3e3216376386938e1886
7
+ data.tar.gz: 1b403888aa4428770f105a2e415997a6460e0cfb25335ba50a29e34add5cee35611ce138298e4e2e5276a4d16e8ea4fd7bc18f205aca15edc835d9e7f1773e02
data/README.md CHANGED
@@ -75,10 +75,24 @@ Likewise, you can specify Windows command prompt test by `Yamatanooroti::Windows
75
75
 
76
76
  ## Method Reference
77
77
 
78
- ### `start_terminal(height, width, command)`
78
+ ### `start_terminal(height, width, command, startup_message: nil)`
79
79
 
80
80
  Starts terminal internally that is sized `height` and `width` with `command` to test the result. The `command` should be an array of strings with a path of command and zero or more options. This should be called in `setup` method.
81
81
 
82
+ If `startup_message` is given, `start_terminal` waits for the string to be printed and then returns.
83
+
84
+ ```ruby
85
+ code = 'sleep 1; puts "aaa"; sleep 10; puts "bbb"'
86
+ start_terminal(5, 30, ['ruby', '-e', code], startup_message: 'aaa')
87
+ close
88
+ assert_screen(<<~EOC)
89
+ aaa
90
+ EOC
91
+ # The start_terminal method waits for the output of the "aaa" as specified by
92
+ # the startup_message option, the "bbb" after 10 seconds won't come because
93
+ # the I/O is closed immediately after it.
94
+ ```
95
+
82
96
  ### `write(str)`
83
97
 
84
98
  Writes `str` like inputting by a keyboard to the started terminal.
@@ -78,9 +78,8 @@ class Yamatanooroti::TestCase < Test::Unit::TestCase
78
78
  def klass.method_added(name)
79
79
  super
80
80
  if ancestors[1] == Yamatanooroti::TestCase
81
- @@runners.each do |test_klass|
82
- test_klass.define_method(name, instance_method(name))
83
- end
81
+ test_klass = @@runners.find { |test_klass| test_klass.ancestors.include?(self) }
82
+ test_klass.define_method(name, instance_method(name))
84
83
  remove_method name
85
84
  end
86
85
  end
@@ -1,3 +1,3 @@
1
1
  class Yamatanooroti
2
- VERSION = '0.0.1'
2
+ VERSION = '0.0.6'
3
3
  end
@@ -1,12 +1,14 @@
1
1
  require 'test-unit'
2
2
  require 'vterm'
3
3
  require 'pty'
4
+ require 'io/console'
4
5
 
5
6
  module Yamatanooroti::VTermTestCaseModule
6
- def start_terminal(height, width, command, wait: 0.1)
7
+ def start_terminal(height, width, command, wait: 0.1, startup_message: nil)
7
8
  @wait = wait
9
+ @result = nil
8
10
 
9
- @pty_output, @pty_input, @pid = PTY.spawn(*command)
11
+ @pty_output, @pty_input, @pid = PTY.spawn('bash', '-c', %[stty rows #{height.to_s} cols #{width.to_s}; "$@"], '--', *command)
10
12
 
11
13
  @vterm = VTerm.new(height, width)
12
14
  @vterm.set_utf8(true)
@@ -14,6 +16,15 @@ module Yamatanooroti::VTermTestCaseModule
14
16
  @screen = @vterm.screen
15
17
  @screen.reset(true)
16
18
 
19
+ case startup_message
20
+ when String
21
+ @startup_message = ->(message) { message.start_with?(startup_message) }
22
+ when Regexp
23
+ @startup_message = ->(message) { startup_message.match?(message) }
24
+ else
25
+ @startup_message = nil
26
+ end
27
+
17
28
  sync
18
29
  end
19
30
 
@@ -27,35 +38,56 @@ module Yamatanooroti::VTermTestCaseModule
27
38
  sync
28
39
  @pty_input.close
29
40
  sync
41
+ Process.kill('KILL', @pid)
42
+ Process.waitpid(@pid)
30
43
  end
31
44
 
32
45
  private def sync
46
+ startup_message = '' if @startup_message
33
47
  loop do
34
48
  sleep @wait
35
49
  chunk = @pty_output.read_nonblock(1024)
50
+ if @startup_message
51
+ startup_message << chunk
52
+ if @startup_message.(startup_message)
53
+ @startup_message = nil
54
+ chunk = startup_message
55
+ else
56
+ redo
57
+ end
58
+ end
36
59
  @vterm.write(chunk)
37
60
  chunk = @vterm.read
38
61
  @pty_input.write(chunk)
39
62
  rescue Errno::EAGAIN, Errno::EWOULDBLOCK
63
+ retry if @startup_message
40
64
  break
41
65
  rescue Errno::EIO # EOF
66
+ retry if @startup_message
42
67
  break
43
68
  rescue IO::EAGAINWaitReadable # emtpy buffer
69
+ retry if @startup_message
44
70
  break
45
71
  end
46
72
  end
47
73
 
48
- def assert_screen(expected_lines)
49
- actual_lines = []
74
+ def result
75
+ return @result if @result
76
+ @result = []
50
77
  rows, cols = @vterm.size
51
78
  rows.times do |r|
52
- actual_lines << ''
79
+ @result << ''
53
80
  cols.times do |c|
54
81
  cell = @screen.cell_at(r, c)
55
- actual_lines.last << cell.char if cell.char
82
+ @result.last << cell.char if cell.char
56
83
  end
57
- actual_lines.last.gsub!(/ *$/, '')
84
+ @result.last.gsub!(/ *$/, '')
58
85
  end
86
+ @result
87
+ end
88
+
89
+ def assert_screen(expected_lines)
90
+ actual_lines = result
59
91
  case expected_lines
60
92
  when Array
61
93
  assert_equal(expected_lines, actual_lines)
@@ -0,0 +1,70 @@
1
+ require 'test-unit'
2
+ require 'vterm'
3
+ require 'pty'
4
+
5
+ module Yamatanooroti::VTermTestCaseModule
6
+ def start_terminal(height, width, command, wait: 0.1)
7
+ @wait = wait
8
+
9
+ @pty_output, @pty_input, @pid = PTY.spawn(*command)
10
+
11
+ @vterm = VTerm.new(height, width)
12
+ @vterm.set_utf8(true)
13
+
14
+ @screen = @vterm.screen
15
+ @screen.reset(true)
16
+
17
+ sync
18
+ end
19
+
20
+ def write(str)
21
+ sync
22
+ @pty_input.write(str)
23
+ sync
24
+ end
25
+
26
+ def close
27
+ sync
28
+ @pty_input.close
29
+ sync
30
+ end
31
+
32
+ private def sync
33
+ loop do
34
+ sleep @wait
35
+ chunk = @pty_output.read_nonblock(1024)
36
+ @vterm.write(chunk)
37
+ chunk = @vterm.read
38
+ @pty_input.write(chunk)
39
+ rescue Errno::EAGAIN, Errno::EWOULDBLOCK
40
+ break
41
+ rescue Errno::EIO # EOF
42
+ break
43
+ rescue IO::EAGAINWaitReadable # emtpy buffer
44
+ break
45
+ end
46
+ end
47
+
48
+ def assert_screen(expected_lines)
49
+ actual_lines = []
50
+ rows, cols = @vterm.size
51
+ rows.times do |r|
52
+ actual_lines << ''
53
+ cols.times do |c|
54
+ cell = @screen.cell_at(r, c)
55
+ actual_lines.last << cell.char if cell.char
56
+ end
57
+ actual_lines.last.gsub!(/ *$/, '')
58
+ end
59
+ case expected_lines
60
+ when Array
61
+ assert_equal(expected_lines, actual_lines)
62
+ when String
63
+ assert_equal(expected_lines, actual_lines.join("\n").sub(/\n*\z/, "\n"))
64
+ end
65
+ end
66
+ end
67
+
68
+ class Yamatanooroti::VTermTestCase < Test::Unit::TestCase
69
+ include Yamatanooroti::VTermTestCaseModule
70
+ end
@@ -11,7 +11,7 @@ module Yamatanooroti::WindowsDefinition
11
11
 
12
12
  typealias 'SHORT', 'short'
13
13
  typealias 'HPCON', 'HANDLE'
14
- typealias 'HPCON', 'HANDLE'
14
+ typealias 'HWND', 'HANDLE'
15
15
  typealias 'HRESULT', 'HANDLE'
16
16
  typealias 'LPVOID', 'void*'
17
17
  typealias 'SIZE_T', 'size_t'
@@ -146,6 +146,7 @@ module Yamatanooroti::WindowsDefinition
146
146
  C3_IDEOGRAPH = 0x0100
147
147
  TH32CS_SNAPPROCESS = 0x00000002
148
148
  PROCESS_ALL_ACCESS = 0x001FFFFF
149
+ SW_HIDE = 0
149
150
 
150
151
  # HANDLE GetStdHandle(DWORD nStdHandle);
151
152
  extern 'HANDLE GetStdHandle(DWORD);', :stdcall
@@ -158,6 +159,10 @@ module Yamatanooroti::WindowsDefinition
158
159
  extern 'BOOL AllocConsole(void);', :stdcall
159
160
  # BOOL AttachConsole(DWORD dwProcessId);
160
161
  extern 'BOOL AttachConsole(DWORD);', :stdcall
162
+ # BOOL ShowWindow(HWND hWnd, int nCmdShow);
163
+ extern 'BOOL ShowWindow(HWND hWnd,int nCmdShow);', :stdcall
164
+ # HWND WINAPI GetConsoleWindow(void);
165
+ extern 'HWND GetConsoleWindow(void);', :stdcall
161
166
  # BOOL WINAPI SetConsoleScreenBufferSize(HANDLE hConsoleOutput, COORD dwSize);
162
167
  extern 'BOOL SetConsoleScreenBufferSize(HANDLE, COORD);', :stdcall
163
168
  # BOOL WINAPI SetConsoleWindowInfo(HANDLE hConsoleOutput, BOOL bAbsolute, const SMALL_RECT *lpConsoleWindow);
@@ -212,10 +217,10 @@ module Yamatanooroti::WindowsTestCaseModule
212
217
 
213
218
  private def setup_console(height, width)
214
219
 
215
- result = DL.FreeConsole
216
- error_message(result, 'FreeConsole')
217
- result = DL.AllocConsole
218
- error_message(result, 'AllocConsole')
220
+ r = DL.FreeConsole
221
+ error_message(r, 'FreeConsole')
222
+ r = DL.AllocConsole
223
+ error_message(r, 'AllocConsole')
219
224
  @output_handle = DL.GetStdHandle(DL::STD_OUTPUT_HANDLE)
220
225
 
221
226
  =begin
@@ -228,8 +233,8 @@ module Yamatanooroti::WindowsTestCaseModule
228
233
  font.FontFamily = 0
229
234
  font.FontWeight = 0
230
235
  font.FaceName[0] = "\x00".ord
231
- result = DL.SetCurrentConsoleFontEx(@output_handle, 0, font)
232
- error_message(result, 'SetCurrentConsoleFontEx')
236
+ r = DL.SetCurrentConsoleFontEx(@output_handle, 0, font)
237
+ error_message(r, 'SetCurrentConsoleFontEx')
233
238
  =end
234
239
 
235
240
  rect = DL::SMALL_RECT.malloc
@@ -237,16 +242,18 @@ module Yamatanooroti::WindowsTestCaseModule
237
242
  rect.Top = 0
238
243
  rect.Right = width - 1
239
244
  rect.Bottom = height - 1
240
- result = DL.SetConsoleWindowInfo(@output_handle, 1, rect)
241
- error_message(result, 'SetConsoleWindowInfo')
245
+ r = DL.SetConsoleWindowInfo(@output_handle, 1, rect)
246
+ error_message(r, 'SetConsoleWindowInfo')
242
247
 
243
248
  size = DL.GetSystemMetrics(DL::SM_CYMIN) * 65536 + DL.GetSystemMetrics(DL::SM_CXMIN)
244
- result = DL.SetConsoleScreenBufferSize(@output_handle, size)
245
- error_message(result, 'SetConsoleScreenBufferSize')
249
+ r = DL.SetConsoleScreenBufferSize(@output_handle, size)
250
+ error_message(r, 'SetConsoleScreenBufferSize')
246
251
 
247
252
  size = height * 65536 + width
248
- result = DL.SetConsoleScreenBufferSize(@output_handle, size)
249
- error_message(result, 'SetConsoleScreenBufferSize')
253
+ r = DL.SetConsoleScreenBufferSize(@output_handle, size)
254
+ error_message(r, 'SetConsoleScreenBufferSize')
255
+ r = DL.ShowWindow(DL.GetConsoleWindow(), DL::SW_HIDE)
256
+ error_message(r, 'ShowWindow')
250
257
  end
251
258
 
252
259
  private def mb2wc(str)
@@ -282,26 +289,55 @@ module Yamatanooroti::WindowsTestCaseModule
282
289
  end
283
290
  end
284
291
 
292
+ private def quote_command_arg(arg)
293
+ if not arg.match?(/[ \t"]/)
294
+ # No quotation needed.
295
+ return arg
296
+ end
297
+
298
+ if not arg.match?(/["\\]/)
299
+ # No embedded double quotes or backlashes, so I can just wrap quote
300
+ # marks around the whole thing.
301
+ return %{"#{arg}"}
302
+ end
303
+
304
+ quote_hit = true
305
+ result = '"'
306
+ arg.chars.reverse.each do |c|
307
+ result << c
308
+ if quote_hit and c == '\\'
309
+ result << '\\'
310
+ elsif c == '"'
311
+ quote_hit = true
312
+ result << '\\'
313
+ else
314
+ quote_hit = false
315
+ end
316
+ end
317
+ result << '"'
318
+ result.reverse
319
+ end
320
+
285
321
  private def launch(command)
286
- command = %Q{cmd.exe /q /c "#{command.gsub('"', '\\"')}"}
322
+ command = %Q{cmd.exe /q /c "#{command}"}
287
323
  converted_command = mb2wc(command)
288
324
  @pi = DL::PROCESS_INFORMATION.malloc
289
325
  (@pi.to_ptr + 0)[0, DL::PROCESS_INFORMATION.size] = "\x00" * DL::PROCESS_INFORMATION.size
290
326
  @startup_info_ex = DL::STARTUPINFOW.malloc
291
327
  (@startup_info_ex.to_ptr + 0)[0, DL::STARTUPINFOW.size] = "\x00" * DL::STARTUPINFOW.size
292
- result = DL.CreateProcessW(
328
+ r = DL.CreateProcessW(
293
329
  Fiddle::NULL, converted_command,
294
330
  Fiddle::NULL, Fiddle::NULL, 0, 0, Fiddle::NULL, Fiddle::NULL,
295
331
  @startup_info_ex, @pi
296
332
  )
297
- error_message(result, 'CreateProcessW')
333
+ error_message(r, 'CreateProcessW')
298
334
  sleep @wait
299
335
  rescue => e
300
336
  pp e
301
337
  end
302
338
 
303
- private def error_message(result, method_name)
304
- return if not result.zero?
339
+ private def error_message(r, method_name)
340
+ return if not r.zero?
305
341
  err = DL.GetLastError
306
342
  string = Fiddle::Pointer.malloc(Fiddle::SIZEOF_VOIDP)
307
343
  DL.FormatMessage(
@@ -349,8 +385,8 @@ module Yamatanooroti::WindowsTestCaseModule
349
385
  r.dwControlKeyState = 0
350
386
  end
351
387
  written_size = Fiddle::Pointer.malloc(Fiddle::SIZEOF_DWORD, DL::FREE)
352
- result = DL.WriteConsoleInputW(DL.GetStdHandle(DL::STD_INPUT_HANDLE), records, str.size * 2, written_size)
353
- error_message(result, 'WriteConsoleInput')
388
+ r = DL.WriteConsoleInputW(DL.GetStdHandle(DL::STD_INPUT_HANDLE), records, str.size * 2, written_size)
389
+ error_message(r, 'WriteConsoleInput')
354
390
  end
355
391
 
356
392
  private def free_resources
@@ -358,41 +394,58 @@ module Yamatanooroti::WindowsTestCaseModule
358
394
  pe = DL::PROCESSENTRY32W.malloc
359
395
  (pe.to_ptr + 0)[0, DL::PROCESSENTRY32W.size] = "\x00" * DL::PROCESSENTRY32W.size
360
396
  pe.dwSize = DL::PROCESSENTRY32W.size
361
- result = DL.Process32FirstW(h_snap, pe)
362
- error_message(result, "Process32First")
363
- result = DL.FreeConsole()
364
- #error_message(result, "FreeConsole")
365
- result = DL.AttachConsole(DL::ATTACH_PARENT_PROCESS)
366
- error_message(result, 'AttachConsole')
397
+ r = DL.Process32FirstW(h_snap, pe)
398
+ error_message(r, "Process32First")
399
+ process_table = {}
367
400
  loop do
368
401
  #log "a #{pe.th32ParentProcessID.inspect} -> #{pe.th32ProcessID.inspect} #{wc2mb(pe.szExeFile.pack('S260')).unpack('Z*').pack('Z*')}"
369
- if pe.th32ParentProcessID == DL.GetCurrentProcessId
370
- h_child_proc = DL.OpenProcess(DL::PROCESS_ALL_ACCESS, 0, pe.th32ProcessID)
371
- if (h_child_proc)
372
- result = DL.TerminateProcess(h_child_proc, 0)
373
- error_message(result, "TerminateProcess")
374
- result = DL.CloseHandle(h_child_proc)
375
- error_message(result, "CloseHandle")
376
- end
377
- end
402
+ process_table[pe.th32ParentProcessID] ||= []
403
+ process_table[pe.th32ParentProcessID] << pe.th32ProcessID
378
404
  break if DL.Process32NextW(h_snap, pe).zero?
379
405
  end
380
- result = DL.TerminateThread(@pi.hThread, 0)
381
- error_message(result, "TerminateThread")
406
+ process_table[DL.GetCurrentProcessId].each do |child_pid|
407
+ kill_process_tree(process_table, child_pid)
408
+ end
409
+ #r = DL.TerminateThread(@pi.hThread, 0)
410
+ #error_message(r, "TerminateThread")
411
+ #sleep @wait
412
+ r = DL.FreeConsole()
413
+ #error_message(r, "FreeConsole")
414
+ r = DL.AttachConsole(DL::ATTACH_PARENT_PROCESS)
415
+ error_message(r, 'AttachConsole')
416
+ end
417
+
418
+ private def kill_process_tree(process_table, pid)
419
+ process_table[pid]&.each do |child_pid|
420
+ kill_process_tree(process_table, child_pid)
421
+ end
422
+ h_proc = DL.OpenProcess(DL::PROCESS_ALL_ACCESS, 0, pid)
423
+ if (h_proc)
424
+ r = DL.TerminateProcess(h_proc, 0)
425
+ error_message(r, "TerminateProcess")
426
+ r = DL.CloseHandle(h_proc)
427
+ error_message(r, "CloseHandle")
428
+ end
382
429
  end
383
430
 
384
431
  def close
385
432
  sleep @wait
386
433
  # read first before kill the console process including output
434
+ @result = retrieve_screen
435
+
436
+ free_resources
437
+ end
438
+
439
+ private def retrieve_screen
387
440
  char_info_matrix = Fiddle::Pointer.to_ptr("\x00" * (DL::CHAR_INFO.size * (@height * @width)))
388
441
  region = DL::SMALL_RECT.malloc
389
442
  region.Left = 0
390
443
  region.Top = 0
391
444
  region.Right = @width
392
445
  region.Bottom = @height
393
- result = DL.ReadConsoleOutputW(@output_handle, char_info_matrix, @height * 65536 + @width, 0, region)
394
- error_message(result, "ReadConsoleOutputW")
395
- @result = []
446
+ r = DL.ReadConsoleOutputW(@output_handle, char_info_matrix, @height * 65536 + @width, 0, region)
447
+ error_message(r, "ReadConsoleOutputW")
448
+ screen = []
396
449
  prev_c = nil
397
450
  @height.times do |y|
398
451
  line = ''
@@ -407,10 +460,13 @@ module Yamatanooroti::WindowsTestCaseModule
407
460
  prev_c = mb
408
461
  end
409
462
  end
410
- @result << line.gsub(/ *$/, '')
463
+ screen << line.gsub(/ *$/, '')
411
464
  end
465
+ screen
466
+ end
412
467
 
413
- free_resources
468
+ def result
469
+ @result
414
470
  end
415
471
 
416
472
  def assert_screen(expected_lines)
@@ -422,12 +478,26 @@ module Yamatanooroti::WindowsTestCaseModule
422
478
  end
423
479
  end
424
480
 
425
- def start_terminal(height, width, command, wait: 1)
481
+ def start_terminal(height, width, command, wait: 1, startup_message: nil)
426
482
  @height = height
427
483
  @width = width
428
484
  @wait = wait
485
+ @result = nil
429
486
  setup_console(height, width)
430
- launch(command.join(' '))
487
+ launch(command.map{ |c| quote_command_arg(c) }.join(' '))
488
+ case startup_message
489
+ when String
490
+ check_startup_message = ->(message) { message.start_with?(startup_message) }
491
+ when Regexp
492
+ check_startup_message = ->(message) { startup_message.match?(message) }
493
+ end
494
+ if check_startup_message
495
+ loop do
496
+ screen = retrieve_screen.join("\n").sub(/\n*\z/, "\n")
497
+ break if check_startup_message.(screen)
498
+ sleep @wait
499
+ end
500
+ end
431
501
  end
432
502
  end
433
503
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yamatanooroti
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - aycabta
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-03-21 00:00:00.000000000 Z
11
+ date: 2020-10-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: test-unit
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
41
55
  description: " Yamatanooroti is a multi-platform real(?) terminal test framework.\n"
42
56
  email:
43
57
  - aycabta@gmail.com
@@ -50,12 +64,13 @@ files:
50
64
  - lib/yamatanooroti.rb
51
65
  - lib/yamatanooroti/version.rb
52
66
  - lib/yamatanooroti/vterm.rb
67
+ - lib/yamatanooroti/vterm.rb.orig
53
68
  - lib/yamatanooroti/windows.rb
54
69
  homepage: https://github.com/aycabta/yamatanooroti
55
70
  licenses:
56
71
  - MIT
57
72
  metadata: {}
58
- post_install_message:
73
+ post_install_message:
59
74
  rdoc_options: []
60
75
  require_paths:
61
76
  - lib
@@ -70,8 +85,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
70
85
  - !ruby/object:Gem::Version
71
86
  version: '0'
72
87
  requirements: []
73
- rubygems_version: 3.1.2
74
- signing_key:
88
+ rubygems_version: 3.0.3
89
+ signing_key:
75
90
  specification_version: 4
76
91
  summary: Multi-platform real(?) terminal test framework
77
92
  test_files: []