File: file_descriptor_spec.cr

package info (click to toggle)
crystal 1.14.0%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 24,384 kB
  • sloc: javascript: 6,400; sh: 695; makefile: 269; ansic: 121; python: 105; cpp: 77; xml: 32
file content (200 lines) | stat: -rw-r--r-- 5,196 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
require "../spec_helper"
require "../../support/finalize"

class IO::FileDescriptor
  include FinalizeCounter
end

private def shell_command(command)
  {% if flag?(:win32) %}
    "cmd.exe /c #{Process.quote(command)}"
  {% else %}
    "/bin/sh -c #{Process.quote(command)}"
  {% end %}
end

describe IO::FileDescriptor do
  describe "#initialize" do
    it "handles closed file descriptor gracefully" do
      a, b = IO.pipe
      a.close
      b.close

      fd = IO::FileDescriptor.new(a.fd)
      fd.closed?.should be_true
    end
  end

  it "reopen STDIN with the right mode", tags: %w[slow] do
    code = %q(puts "#{STDIN.blocking} #{STDIN.info.type}")
    compile_source(code) do |binpath|
      `#{shell_command %(#{Process.quote(binpath)} < #{Process.quote(binpath)})}`.chomp.should eq("true File")
      `#{shell_command %(echo "" | #{Process.quote(binpath)})}`.chomp.should eq("#{{{ flag?(:win32) }}} Pipe")
    end
  end

  describe "#tty?" do
    it "returns false for null device" do
      File.open(File::NULL) do |f|
        f.tty?.should be_false
      end
    end

    it "returns false for standard streams redirected to null device", tags: %w[slow] do
      code = %q(print STDIN.tty?, ' ', STDERR.tty?)
      compile_source(code) do |binpath|
        `#{shell_command %(#{Process.quote(binpath)} < #{File::NULL} 2> #{File::NULL})}`.should eq("false false")
      end
    end
  end

  describe "#finalize" do
    it "closes" do
      pipes = [] of IO::FileDescriptor
      assert_finalizes("fd") do
        a, b = IO.pipe
        pipes << b
        a
      end

      expect_raises(IO::Error) do
        pipes.each do |p|
          p.puts "123"
        end
      end
    end

    it "does not flush" do
      with_tempfile "fd-finalize-flush" do |path|
        file = File.new(path, "w")
        file << "foo"
        file.flush
        file << "bar"
        file.finalize

        File.read(path).should eq "foo"
      ensure
        file.try(&.close) rescue nil
      end
    end
  end

  it "opens STDIN in binary mode", tags: %w[slow] do
    code = %q(print STDIN.gets_to_end.includes?('\r'))
    compile_source(code) do |binpath|
      io_in = IO::Memory.new("foo\r\n")
      io_out = IO::Memory.new
      Process.run(binpath, input: io_in, output: io_out)
      io_out.to_s.should eq("true")
    end
  end

  it "opens STDOUT in binary mode", tags: %w[slow] do
    code = %q(puts "foo")
    compile_source(code) do |binpath|
      io = IO::Memory.new
      Process.run(binpath, output: io)
      io.to_s.should eq("foo\n")
    end
  end

  it "opens STDERR in binary mode", tags: %w[slow] do
    code = %q(STDERR.puts "foo")
    compile_source(code) do |binpath|
      io = IO::Memory.new
      Process.run(binpath, error: io)
      io.to_s.should eq("foo\n")
    end
  end

  it "does not close if close_on_finalize is false" do
    pipes = [] of IO::FileDescriptor
    assert_finalizes("fd") do
      a, b = IO.pipe
      a.close_on_finalize = false
      pipes << b
      a
    end

    pipes.each do |p|
      p.puts "123"
    end
  end

  it "reopens" do
    File.open(datapath("test_file.txt")) do |file1|
      File.open(datapath("test_file.ini")) do |file2|
        file2.reopen(file1)
        file2.gets.should eq("Hello World")
      end
    end
  end

  {% unless flag?(:win32) %}
    describe "close_on_exec" do
      it "sets close on exec on the reopened standard descriptors" do
        unless STDIN.fd == Crystal::System::FileDescriptor::STDIN_HANDLE
          STDIN.close_on_exec?.should be_true
        end

        unless STDOUT.fd == Crystal::System::FileDescriptor::STDOUT_HANDLE
          STDOUT.close_on_exec?.should be_true
        end

        unless STDERR.fd == Crystal::System::FileDescriptor::STDERR_HANDLE
          STDERR.close_on_exec?.should be_true
        end
      end

      it "is enabled by default (open)" do
        File.open(datapath("test_file.txt")) do |file|
          file.close_on_exec?.should be_true
        end
      end

      it "is enabled by default (pipe)" do
        IO::FileDescriptor.pipe.each do |fd|
          fd.close_on_exec?.should be_true
          fd.close_on_exec?.should be_true
        end
      end

      it "can be disabled and reenabled" do
        File.open(datapath("test_file.txt")) do |file|
          file.close_on_exec = false
          file.close_on_exec?.should be_false

          file.close_on_exec = true
          file.close_on_exec?.should be_true
        end
      end

      it "is copied on reopen" do
        File.open(datapath("test_file.txt")) do |file1|
          file1.close_on_exec = true

          File.open(datapath("test_file.ini")) do |file2|
            file2.reopen(file1)
            file2.close_on_exec?.should be_true
          end

          file1.close_on_exec = false

          File.open(datapath("test_file.ini")) do |file3|
            file3.reopen(file1)
            file3.close_on_exec?.should be_false
          end
        end
      end
    end
  {% end %}

  typeof(STDIN.noecho { })
  typeof(STDIN.noecho!)
  typeof(STDIN.echo { })
  typeof(STDIN.echo!)
  typeof(STDIN.cooked { })
  typeof(STDIN.cooked!)
  typeof(STDIN.raw { })
  typeof(STDIN.raw!)
end