1begin
2  require 'io/console'
3  require 'test/unit'
4  require 'pty'
5rescue LoadError
6end
7require_relative '../../ruby/envutil'
8
9class TestIO_Console < Test::Unit::TestCase
10  Bug6116 = '[ruby-dev:45309]'
11
12  def test_raw
13    helper {|m, s|
14      s.print "abc\n"
15      assert_equal("abc\r\n", m.gets)
16      assert_send([s, :echo?])
17      s.raw {
18        assert_not_send([s, :echo?], Bug6116)
19        s.print "def\n"
20        assert_equal("def\n", m.gets)
21      }
22      assert_send([s, :echo?])
23      s.print "ghi\n"
24      assert_equal("ghi\r\n", m.gets)
25    }
26  end
27
28  def test_raw_minchar
29    len = 0
30    th = nil
31    helper {|m, s|
32      assert_equal([nil, 0], [s.getch(min: 0), len])
33      main = Thread.current
34      go = false
35      th = Thread.start {
36        len += 1
37        m.print("a")
38        m.flush
39        sleep 0.01 until go and main.stop?
40        len += 10
41        m.print("1234567890")
42        m.flush
43      }
44      assert_equal(["a", 1], [s.getch(min: 1), len])
45      go = true
46      assert_equal(["1", 11], [s.getch, len])
47    }
48  ensure
49    th.kill if th and th.alive?
50  end
51
52  def test_raw_timeout
53    len = 0
54    th = nil
55    helper {|m, s|
56      assert_equal([nil, 0], [s.getch(min: 0, time: 0.1), len])
57      main = Thread.current
58      th = Thread.start {
59        sleep 0.01 until main.stop?
60        len += 2
61        m.print("ab")
62      }
63      assert_equal(["a", 2], [s.getch(min: 1, time: 1), len])
64      assert_equal(["b", 2], [s.getch(time: 1), len])
65    }
66  ensure
67    th.kill if th and th.alive?
68  end
69
70  def test_cooked
71    helper {|m, s|
72      assert_send([s, :echo?])
73      s.raw {
74        s.print "abc\n"
75        assert_equal("abc\n", m.gets)
76        assert_not_send([s, :echo?], Bug6116)
77        s.cooked {
78          assert_send([s, :echo?])
79          s.print "def\n"
80          assert_equal("def\r\n", m.gets)
81        }
82        assert_not_send([s, :echo?], Bug6116)
83      }
84      assert_send([s, :echo?])
85      s.print "ghi\n"
86      assert_equal("ghi\r\n", m.gets)
87    }
88  end
89
90  def test_echo
91    helper {|m, s|
92      assert_send([s, :echo?])
93      m.print "a"
94      assert_equal("a", m.readpartial(10))
95    }
96  end
97
98  def test_noecho
99    helper {|m, s|
100      s.noecho {
101	assert_not_send([s, :echo?])
102	m.print "a"
103	sleep 0.1
104      }
105      m.print "b"
106      assert_equal("b", m.readpartial(10))
107    }
108  end
109
110  def test_noecho2
111    helper {|m, s|
112      assert_send([s, :echo?])
113      m.print "a\n"
114      sleep 0.1
115      s.print "b\n"
116      sleep 0.1
117      assert_equal("a\r\nb\r\n", m.readpartial(10))
118      assert_equal("a\n", s.readpartial(10))
119      s.noecho {
120        assert_not_send([s, :echo?])
121        m.print "a\n"
122        s.print "b\n"
123        assert_equal("b\r\n", m.readpartial(10))
124        assert_equal("a\n", s.readpartial(10))
125      }
126      assert_send([s, :echo?])
127      m.print "a\n"
128      sleep 0.1
129      s.print "b\n"
130      sleep 0.1
131      assert_equal("a\r\nb\r\n", m.readpartial(10))
132      assert_equal("a\n", s.readpartial(10))
133    }
134  end
135
136  def test_setecho
137    helper {|m, s|
138      assert_send([s, :echo?])
139      s.echo = false
140      m.print "a"
141      sleep 0.1
142      s.echo = true
143      m.print "b"
144      assert_equal("b", m.readpartial(10))
145    }
146  end
147
148  def test_setecho2
149    helper {|m, s|
150      assert_send([s, :echo?])
151      m.print "a\n"
152      sleep 0.1
153      s.print "b\n"
154      sleep 0.1
155      assert_equal("a\r\nb\r\n", m.readpartial(10))
156      assert_equal("a\n", s.readpartial(10))
157      s.echo = false
158      assert_not_send([s, :echo?])
159      m.print "a\n"
160      s.print "b\n"
161      assert_equal("b\r\n", m.readpartial(10))
162      assert_equal("a\n", s.readpartial(10))
163      s.echo = true
164      assert_send([s, :echo?])
165      m.print "a\n"
166      sleep 0.1
167      s.print "b\n"
168      sleep 0.1
169      assert_equal("a\r\nb\r\n", m.readpartial(10))
170      assert_equal("a\n", s.readpartial(10))
171    }
172  end
173
174  def test_iflush
175    helper {|m, s|
176      m.print "a"
177      s.iflush
178      m.print "b\n"
179      assert_equal("b\n", s.readpartial(10))
180    }
181  end
182
183  def test_oflush
184    helper {|m, s|
185      s.print "a"
186      s.oflush # oflush may be issued after "a" is already sent.
187      s.print "b"
188      assert_include(["b", "ab"], m.readpartial(10))
189    }
190  end
191
192  def test_ioflush
193    helper {|m, s|
194      m.print "a"
195      s.ioflush
196      m.print "b\n"
197      assert_equal("b\n", s.readpartial(10))
198    }
199  end
200
201  def test_ioflush2
202    helper {|m, s|
203      s.print "a"
204      s.ioflush # ioflush may be issued after "a" is already sent.
205      s.print "b"
206      assert_include(["b", "ab"], m.readpartial(10))
207    }
208  end
209
210  def test_winsize
211    helper {|m, s|
212      begin
213        assert_equal([0, 0], s.winsize)
214      rescue Errno::EINVAL # OpenSolaris 2009.06 TIOCGWINSZ causes Errno::EINVAL before TIOCSWINSZ.
215      end
216    }
217  end
218
219  if IO.console
220    def test_sync
221      assert(IO.console.sync, "console should be unbuffered")
222    end
223  else
224    def test_sync
225      r, w, pid = PTY.spawn(EnvUtil.rubybin, "-rio/console", "-e", "p IO.console.class")
226    rescue RuntimeError
227      skip $!
228    else
229      con = r.gets.chomp
230      Process.wait(pid)
231      assert_match("File", con)
232    end
233  end
234
235  private
236  def helper
237    m, s = PTY.open
238  rescue RuntimeError
239    skip $!
240  else
241    yield m, s
242  ensure
243    m.close if m
244    s.close if s
245  end
246end if defined?(PTY) and defined?(IO::console)
247
248class TestIO_Console < Test::Unit::TestCase
249  case
250  when Process.respond_to?(:daemon)
251    noctty = [EnvUtil.rubybin, "-e", "Process.daemon(true)"]
252  when !(rubyw = RbConfig::CONFIG["RUBYW_INSTALL_NAME"]).empty?
253    dir, base = File.split(EnvUtil.rubybin)
254    noctty = [File.join(dir, base.sub(/ruby/, rubyw))]
255  end
256
257  if noctty
258    require 'tempfile'
259    NOCTTY = noctty
260    def test_noctty
261      t = Tempfile.new("console")
262      t.close
263      t2 = Tempfile.new("console")
264      t2.close
265      cmd = NOCTTY + [
266        '--disable=gems',
267        '-rio/console',
268        '-e', 'open(ARGV[0], "w") {|f| f.puts IO.console.inspect}',
269        '-e', 'File.unlink(ARGV[1])',
270        '--', t.path, t2.path]
271      system(*cmd)
272      sleep 0.1 while File.exist?(t2.path)
273      t.open
274      assert_equal("nil", t.gets.chomp)
275    ensure
276      t.close! if t and !t.closed?
277      t2.close!
278    end
279  end
280end if defined?(IO.console)
281
282class TestIO_Console < Test::Unit::TestCase
283  def test_stringio_getch
284    assert_separately %w"--disable=gems -rstringio -rio/console", %q{
285      assert_operator(StringIO, :method_defined?, :getch)
286    }
287    assert_separately %w"--disable=gems -rio/console -rstringio", %q{
288      assert_operator(StringIO, :method_defined?, :getch)
289    }
290    assert_separately %w"--disable=gems -rstringio", %q{
291      assert_not_operator(StringIO, :method_defined?, :getch)
292    }
293  end
294end
295