summaryrefslogtreecommitdiff
path: root/spec/ruby/core/kernel/system_spec.rb
blob: 9bc03924dd883d69cba14ced6ba59cbd73e0fd74 (plain)
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
require_relative '../../spec_helper'
require_relative 'fixtures/classes'

describe :kernel_system, shared: true do
  it "executes the specified command in a subprocess" do
    -> { @object.system("echo a") }.should output_to_fd("a\n")

    $?.should be_an_instance_of Process::Status
    $?.should.success?
  end

  it "returns true when the command exits with a zero exit status" do
    @object.system(ruby_cmd('exit 0')).should == true

    $?.should be_an_instance_of Process::Status
    $?.should.success?
    $?.exitstatus.should == 0
  end

  it "returns false when the command exits with a non-zero exit status" do
    @object.system(ruby_cmd('exit 1')).should == false

    $?.should be_an_instance_of Process::Status
    $?.should_not.success?
    $?.exitstatus.should == 1
  end

  it "raises RuntimeError when `exception: true` is given and the command exits with a non-zero exit status" do
    -> { @object.system(ruby_cmd('exit 1'), exception: true) }.should raise_error(RuntimeError)
  end

  it "raises Errno::ENOENT when `exception: true` is given and the specified command does not exist" do
    -> { @object.system('feature_14386', exception: true) }.should raise_error(Errno::ENOENT)
  end

  it "returns nil when command execution fails" do
    @object.system("sad").should be_nil

    $?.should be_an_instance_of Process::Status
    $?.pid.should be_kind_of(Integer)
    $?.should_not.success?
  end

  it "does not write to stderr when command execution fails" do
    -> { @object.system("sad") }.should output_to_fd("", STDERR)
  end

  platform_is_not :windows do
    before :each do
      @shell = ENV['SHELL']
    end

    after :each do
      ENV['SHELL'] = @shell
    end

    it "executes with `sh` if the command contains shell characters" do
      -> { @object.system("echo $0") }.should output_to_fd("sh\n")
    end

    it "ignores SHELL env var and always uses `sh`" do
      ENV['SHELL'] = "/bin/fakeshell"
      -> { @object.system("echo $0") }.should output_to_fd("sh\n")
    end
  end

  platform_is_not :windows do
    before :each do
      require 'tmpdir'
      @shell_command = File.join(Dir.mktmpdir, "noshebang.cmd")
      File.write(@shell_command, %[echo "$PATH"\n], perm: 0o700)
    end

    after :each do
      File.unlink(@shell_command)
      Dir.rmdir(File.dirname(@shell_command))
    end

    it "executes with `sh` if the command is executable but not binary and there is no shebang" do
      -> { @object.system(@shell_command) }.should output_to_fd(ENV['PATH'] + "\n")
    end
  end

  before :each do
    ENV['TEST_SH_EXPANSION'] = 'foo'
    @shell_var = '$TEST_SH_EXPANSION'
    platform_is :windows do
      @shell_var = '%TEST_SH_EXPANSION%'
    end
  end

  after :each do
    ENV.delete('TEST_SH_EXPANSION')
  end

  it "expands shell variables when given a single string argument" do
    -> { @object.system("echo #{@shell_var}") }.should output_to_fd("foo\n")
  end

  platform_is_not :windows do
    it "does not expand shell variables when given multiples arguments" do
      -> { @object.system("echo", @shell_var) }.should output_to_fd("#{@shell_var}\n")
    end
  end

  platform_is :windows do
    it "does expand shell variables when given multiples arguments" do
      # See https://bugs.ruby-lang.org/issues/12231
      -> { @object.system("echo", @shell_var) }.should output_to_fd("foo\n")
    end
  end

  platform_is :windows do
    it "runs commands starting with any number of @ using shell" do
      `#{ruby_cmd("p system 'does_not_exist'")} 2>NUL`.chomp.should == "nil"
      @object.system('@does_not_exist 2>NUL').should == false
      @object.system("@@@#{ruby_cmd('exit 0')}").should == true
    end
  end
end

describe "Kernel#system" do
  it "is a private method" do
    Kernel.should have_private_instance_method(:system)
  end

  it_behaves_like :kernel_system, :system, KernelSpecs::Method.new
end

describe "Kernel.system" do
  it_behaves_like :kernel_system, :system, Kernel
end