yamatanooroti 0.0.1 → 0.0.6

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