yamatanooroti 0.0.2 → 0.0.7

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: 92dcb67921820e4dedad1aed9de7d18c062f623c9b73c4a05427049111217f4a
4
- data.tar.gz: 5683fb3c7544c67dc4c1f024df1b8fd9953c6378be0826a6f834191cf5c694e8
3
+ metadata.gz: b0fd9df2f9f10fe273c68653443f62f80a66b2c18e2f7fad4ad3a39bf2b41ff5
4
+ data.tar.gz: e349135272b3c4321e8e7a54d03fe8e22c5a3e237d1b6c134743f44d94ccb628
5
5
  SHA512:
6
- metadata.gz: 6c614da80dcaf5b4ff0799be9be726fa0ea61fcc8ae1f53a4219e61cb17a86d3dee3e9e3fce5663b7224493b53ee0f379359859f448b10963306959647169c16
7
- data.tar.gz: 0e2d1c4d031666509d85c5abc5a8d0f847d3c50392aec52df6f920269ebdff7fb99ac0442c1e582e8edd06f74f124dc9de559a5db8bc271510b331afbdc0f53b
6
+ metadata.gz: 6d5612862c2a2286463a81af6b1cce97c95fc6fe8f231c8e1cd78a03c5ea4d5a582f51d5bb80f13f80c7fa3717db242c35e13f4d23fb4e846d7a38484336d860
7
+ data.tar.gz: e891f544e3433528a6ca47a49538fd4a571cbe8f9eca874091df1cec177ff9ba2199718ffa0cd6fc291d82d6ae0c8f2195ed99adbb24cf4206e3ea9b61e86ee8
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.
data/lib/yamatanooroti.rb CHANGED
@@ -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.2'
2
+ VERSION = '0.0.7'
3
3
  end
@@ -1,13 +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
8
9
  @result = nil
9
10
 
10
- @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)
11
12
 
12
13
  @vterm = VTerm.new(height, width)
13
14
  @vterm.set_utf8(true)
@@ -15,12 +16,32 @@ module Yamatanooroti::VTermTestCaseModule
15
16
  @screen = @vterm.screen
16
17
  @screen.reset(true)
17
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
+
18
28
  sync
19
29
  end
20
30
 
21
31
  def write(str)
22
32
  sync
23
- @pty_input.write(str)
33
+ str_to_write = String.new(encoding: Encoding::ASCII_8BIT)
34
+ str.chars.each do |c|
35
+ byte = c.force_encoding(Encoding::ASCII_8BIT).ord
36
+ if c.bytesize == 1 and byte.allbits?(0x80) # with Meta key
37
+ c = (byte ^ 0x80).chr
38
+ str_to_write << "\e"
39
+ str_to_write << c
40
+ else
41
+ str_to_write << c
42
+ end
43
+ end
44
+ @pty_input.write(str_to_write)
24
45
  sync
25
46
  end
26
47
 
@@ -29,20 +50,34 @@ module Yamatanooroti::VTermTestCaseModule
29
50
  @pty_input.close
30
51
  sync
31
52
  Process.kill('KILL', @pid)
53
+ Process.waitpid(@pid)
32
54
  end
33
55
 
34
56
  private def sync
57
+ startup_message = '' if @startup_message
35
58
  loop do
36
59
  sleep @wait
37
60
  chunk = @pty_output.read_nonblock(1024)
61
+ if @startup_message
62
+ startup_message << chunk
63
+ if @startup_message.(startup_message)
64
+ @startup_message = nil
65
+ chunk = startup_message
66
+ else
67
+ redo
68
+ end
69
+ end
38
70
  @vterm.write(chunk)
39
71
  chunk = @vterm.read
40
72
  @pty_input.write(chunk)
41
73
  rescue Errno::EAGAIN, Errno::EWOULDBLOCK
74
+ retry if @startup_message
42
75
  break
43
76
  rescue Errno::EIO # EOF
77
+ retry if @startup_message
44
78
  break
45
79
  rescue IO::EAGAINWaitReadable # emtpy buffer
80
+ retry if @startup_message
46
81
  break
47
82
  end
48
83
  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,8 @@ module Yamatanooroti::WindowsDefinition
146
146
  C3_IDEOGRAPH = 0x0100
147
147
  TH32CS_SNAPPROCESS = 0x00000002
148
148
  PROCESS_ALL_ACCESS = 0x001FFFFF
149
+ SW_HIDE = 0
150
+ LEFT_ALT_PRESSED = 0x0002
149
151
 
150
152
  # HANDLE GetStdHandle(DWORD nStdHandle);
151
153
  extern 'HANDLE GetStdHandle(DWORD);', :stdcall
@@ -158,12 +160,20 @@ module Yamatanooroti::WindowsDefinition
158
160
  extern 'BOOL AllocConsole(void);', :stdcall
159
161
  # BOOL AttachConsole(DWORD dwProcessId);
160
162
  extern 'BOOL AttachConsole(DWORD);', :stdcall
163
+ # BOOL ShowWindow(HWND hWnd, int nCmdShow);
164
+ extern 'BOOL ShowWindow(HWND hWnd,int nCmdShow);', :stdcall
165
+ # HWND WINAPI GetConsoleWindow(void);
166
+ extern 'HWND GetConsoleWindow(void);', :stdcall
161
167
  # BOOL WINAPI SetConsoleScreenBufferSize(HANDLE hConsoleOutput, COORD dwSize);
162
168
  extern 'BOOL SetConsoleScreenBufferSize(HANDLE, COORD);', :stdcall
163
169
  # BOOL WINAPI SetConsoleWindowInfo(HANDLE hConsoleOutput, BOOL bAbsolute, const SMALL_RECT *lpConsoleWindow);
164
170
  extern 'BOOL SetConsoleWindowInfo(HANDLE, BOOL, PSMALL_RECT);', :stdcall
165
171
  # BOOL WriteConsoleInputW(HANDLE hConsoleInput, const INPUT_RECORD *lpBuffer, DWORD nLength, LPDWORD lpNumberOfEventsWritten);
166
172
  extern 'BOOL WriteConsoleInputW(HANDLE, const INPUT_RECORD*, DWORD, LPDWORD);', :stdcall
173
+ # SHORT VkKeyScanW(WCHAR ch);
174
+ extern 'SHORT VkKeyScanW(WCHAR);', :stdcall
175
+ # UINT MapVirtualKeyW(UINT uCode, UINT uMapType);
176
+ extern 'UINT MapVirtualKeyW(UINT, UINT);', :stdcall
167
177
  # BOOL ReadConsoleOutputW(HANDLE hConsoleOutput, PCHAR_INFO lpBuffer, COORD dwBufferSize, COORD dwBufferCoord, PSMALL_RECT lpReadRegion);
168
178
  extern 'BOOL ReadConsoleOutputW(HANDLE, PCHAR_INFO, COORD, COORD, PSMALL_RECT);', :stdcall
169
179
  # BOOL WINAPI SetCurrentConsoleFontEx(HANDLE hConsoleOutput, BOOL bMaximumWindow, PCONSOLE_FONT_INFOEX lpConsoleCurrentFontEx);
@@ -247,6 +257,8 @@ module Yamatanooroti::WindowsTestCaseModule
247
257
  size = height * 65536 + width
248
258
  r = DL.SetConsoleScreenBufferSize(@output_handle, size)
249
259
  error_message(r, 'SetConsoleScreenBufferSize')
260
+ r = DL.ShowWindow(DL.GetConsoleWindow(), DL::SW_HIDE)
261
+ error_message(r, 'ShowWindow')
250
262
  end
251
263
 
252
264
  private def mb2wc(str)
@@ -282,8 +294,37 @@ module Yamatanooroti::WindowsTestCaseModule
282
294
  end
283
295
  end
284
296
 
297
+ private def quote_command_arg(arg)
298
+ if not arg.match?(/[ \t"]/)
299
+ # No quotation needed.
300
+ return arg
301
+ end
302
+
303
+ if not arg.match?(/["\\]/)
304
+ # No embedded double quotes or backlashes, so I can just wrap quote
305
+ # marks around the whole thing.
306
+ return %{"#{arg}"}
307
+ end
308
+
309
+ quote_hit = true
310
+ result = '"'
311
+ arg.chars.reverse.each do |c|
312
+ result << c
313
+ if quote_hit and c == '\\'
314
+ result << '\\'
315
+ elsif c == '"'
316
+ quote_hit = true
317
+ result << '\\'
318
+ else
319
+ quote_hit = false
320
+ end
321
+ end
322
+ result << '"'
323
+ result.reverse
324
+ end
325
+
285
326
  private def launch(command)
286
- command = %Q{cmd.exe /q /c "#{command.gsub('"', '\\"')}"}
327
+ command = %Q{cmd.exe /q /c "#{command}"}
287
328
  converted_command = mb2wc(command)
288
329
  @pi = DL::PROCESS_INFORMATION.malloc
289
330
  (@pi.to_ptr + 0)[0, DL::PROCESS_INFORMATION.size] = "\x00" * DL::PROCESS_INFORMATION.size
@@ -326,33 +367,43 @@ module Yamatanooroti::WindowsTestCaseModule
326
367
 
327
368
  def write(str)
328
369
  sleep @wait
329
- str.tr!("\n", "\r")
370
+ str.force_encoding(Encoding::ASCII_8BIT).tr!("\n", "\r")
330
371
  records = Fiddle::Pointer.malloc(DL::INPUT_RECORD_WITH_KEY_EVENT.size * str.size * 2, DL::FREE)
331
372
  str.chars.each_with_index do |c, i|
373
+ byte = c.ord
374
+ if c.bytesize == 1 and byte.allbits?(0x80) # with Meta key
375
+ c = (byte ^ 0x80).chr
376
+ control_key_state = DL::LEFT_ALT_PRESSED
377
+ else
378
+ control_key_state = 0
379
+ end
332
380
  record_index = i * 2
333
381
  r = DL::INPUT_RECORD_WITH_KEY_EVENT.new(records + DL::INPUT_RECORD_WITH_KEY_EVENT.size * record_index)
334
- r.EventType = DL::KEY_EVENT
335
- r.bKeyDown = 1
336
- r.wRepeatCount = 0
337
- r.wVirtualKeyCode = 0
338
- r.wVirtualScanCode = 0
339
- r.UnicodeChar = c.unpack('U').first
340
- r.dwControlKeyState = 0
382
+ set_input_record(r, c, true, control_key_state)
341
383
  record_index = i * 2 + 1
342
384
  r = DL::INPUT_RECORD_WITH_KEY_EVENT.new(records + DL::INPUT_RECORD_WITH_KEY_EVENT.size * record_index)
343
- r.EventType = DL::KEY_EVENT
344
- r.bKeyDown = 0
345
- r.wRepeatCount = 0
346
- r.wVirtualKeyCode = 0
347
- r.wVirtualScanCode = 0
348
- r.UnicodeChar = c.unpack('U').first
349
- r.dwControlKeyState = 0
385
+ set_input_record(r, c, false, control_key_state)
350
386
  end
351
387
  written_size = Fiddle::Pointer.malloc(Fiddle::SIZEOF_DWORD, DL::FREE)
352
388
  r = DL.WriteConsoleInputW(DL.GetStdHandle(DL::STD_INPUT_HANDLE), records, str.size * 2, written_size)
353
389
  error_message(r, 'WriteConsoleInput')
354
390
  end
355
391
 
392
+ private def set_input_record(r, c, key_down, control_key_state)
393
+ begin
394
+ code = c.unpack('U').first
395
+ rescue ArgumentError
396
+ code = c.bytes.first
397
+ end
398
+ r.EventType = DL::KEY_EVENT
399
+ r.bKeyDown = key_down ? 1 : 0
400
+ r.wRepeatCount = 1
401
+ r.wVirtualKeyCode = DL.VkKeyScanW(code)
402
+ r.wVirtualScanCode = DL.MapVirtualKeyW(code, 0)
403
+ r.UnicodeChar = code
404
+ r.dwControlKeyState = control_key_state
405
+ end
406
+
356
407
  private def free_resources
357
408
  h_snap = DL.CreateToolhelp32Snapshot(DL::TH32CS_SNAPPROCESS, 0)
358
409
  pe = DL::PROCESSENTRY32W.malloc
@@ -360,31 +411,47 @@ module Yamatanooroti::WindowsTestCaseModule
360
411
  pe.dwSize = DL::PROCESSENTRY32W.size
361
412
  r = DL.Process32FirstW(h_snap, pe)
362
413
  error_message(r, "Process32First")
414
+ process_table = {}
363
415
  loop do
364
416
  #log "a #{pe.th32ParentProcessID.inspect} -> #{pe.th32ProcessID.inspect} #{wc2mb(pe.szExeFile.pack('S260')).unpack('Z*').pack('Z*')}"
365
- if pe.th32ParentProcessID == DL.GetCurrentProcessId
366
- h_child_proc = DL.OpenProcess(DL::PROCESS_ALL_ACCESS, 0, pe.th32ProcessID)
367
- if (h_child_proc)
368
- r = DL.TerminateProcess(h_child_proc, 0)
369
- error_message(r, "TerminateProcess")
370
- r = DL.CloseHandle(h_child_proc)
371
- error_message(r, "CloseHandle")
372
- end
373
- end
417
+ process_table[pe.th32ParentProcessID] ||= []
418
+ process_table[pe.th32ParentProcessID] << pe.th32ProcessID
374
419
  break if DL.Process32NextW(h_snap, pe).zero?
375
420
  end
376
- r = DL.TerminateThread(@pi.hThread, 0)
377
- error_message(r, "TerminateThread")
378
- sleep @wait
421
+ process_table[DL.GetCurrentProcessId].each do |child_pid|
422
+ kill_process_tree(process_table, child_pid)
423
+ end
424
+ #r = DL.TerminateThread(@pi.hThread, 0)
425
+ #error_message(r, "TerminateThread")
426
+ #sleep @wait
379
427
  r = DL.FreeConsole()
380
428
  #error_message(r, "FreeConsole")
381
429
  r = DL.AttachConsole(DL::ATTACH_PARENT_PROCESS)
382
430
  error_message(r, 'AttachConsole')
383
431
  end
384
432
 
433
+ private def kill_process_tree(process_table, pid)
434
+ process_table[pid]&.each do |child_pid|
435
+ kill_process_tree(process_table, child_pid)
436
+ end
437
+ h_proc = DL.OpenProcess(DL::PROCESS_ALL_ACCESS, 0, pid)
438
+ if (h_proc)
439
+ r = DL.TerminateProcess(h_proc, 0)
440
+ error_message(r, "TerminateProcess")
441
+ r = DL.CloseHandle(h_proc)
442
+ error_message(r, "CloseHandle")
443
+ end
444
+ end
445
+
385
446
  def close
386
447
  sleep @wait
387
448
  # read first before kill the console process including output
449
+ @result = retrieve_screen
450
+
451
+ free_resources
452
+ end
453
+
454
+ private def retrieve_screen
388
455
  char_info_matrix = Fiddle::Pointer.to_ptr("\x00" * (DL::CHAR_INFO.size * (@height * @width)))
389
456
  region = DL::SMALL_RECT.malloc
390
457
  region.Left = 0
@@ -393,7 +460,7 @@ module Yamatanooroti::WindowsTestCaseModule
393
460
  region.Bottom = @height
394
461
  r = DL.ReadConsoleOutputW(@output_handle, char_info_matrix, @height * 65536 + @width, 0, region)
395
462
  error_message(r, "ReadConsoleOutputW")
396
- @result = []
463
+ screen = []
397
464
  prev_c = nil
398
465
  @height.times do |y|
399
466
  line = ''
@@ -408,10 +475,9 @@ module Yamatanooroti::WindowsTestCaseModule
408
475
  prev_c = mb
409
476
  end
410
477
  end
411
- @result << line.gsub(/ *$/, '')
478
+ screen << line.gsub(/ *$/, '')
412
479
  end
413
-
414
- free_resources
480
+ screen
415
481
  end
416
482
 
417
483
  def result
@@ -427,13 +493,26 @@ module Yamatanooroti::WindowsTestCaseModule
427
493
  end
428
494
  end
429
495
 
430
- def start_terminal(height, width, command, wait: 1)
496
+ def start_terminal(height, width, command, wait: 1, startup_message: nil)
431
497
  @height = height
432
498
  @width = width
433
499
  @wait = wait
434
500
  @result = nil
435
501
  setup_console(height, width)
436
- launch(command.join(' '))
502
+ launch(command.map{ |c| quote_command_arg(c) }.join(' '))
503
+ case startup_message
504
+ when String
505
+ check_startup_message = ->(message) { message.start_with?(startup_message) }
506
+ when Regexp
507
+ check_startup_message = ->(message) { startup_message.match?(message) }
508
+ end
509
+ if check_startup_message
510
+ loop do
511
+ screen = retrieve_screen.join("\n").sub(/\n*\z/, "\n")
512
+ break if check_startup_message.(screen)
513
+ sleep @wait
514
+ end
515
+ end
437
516
  end
438
517
  end
439
518
 
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.2
4
+ version: 0.0.7
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-07-03 00:00:00.000000000 Z
11
+ date: 2021-04-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: test-unit
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: reline
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
55
69
  description: " Yamatanooroti is a multi-platform real(?) terminal test framework.\n"
56
70
  email:
57
71
  - aycabta@gmail.com
@@ -64,13 +78,12 @@ files:
64
78
  - lib/yamatanooroti.rb
65
79
  - lib/yamatanooroti/version.rb
66
80
  - lib/yamatanooroti/vterm.rb
67
- - lib/yamatanooroti/vterm.rb.orig
68
81
  - lib/yamatanooroti/windows.rb
69
82
  homepage: https://github.com/aycabta/yamatanooroti
70
83
  licenses:
71
84
  - MIT
72
85
  metadata: {}
73
- post_install_message:
86
+ post_install_message:
74
87
  rdoc_options: []
75
88
  require_paths:
76
89
  - lib
@@ -85,8 +98,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
85
98
  - !ruby/object:Gem::Version
86
99
  version: '0'
87
100
  requirements: []
88
- rubygems_version: 3.1.2
89
- signing_key:
101
+ rubygems_version: 3.2.3
102
+ signing_key:
90
103
  specification_version: 4
91
104
  summary: Multi-platform real(?) terminal test framework
92
105
  test_files: []
@@ -1,70 +0,0 @@
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