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 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
|
require "spec"
require "./spec_helper"
describe "PROGRAM_NAME" do
it "works for UTF-8 name", tags: %w[slow] do
with_tempfile("source_file") do |source_file|
if ENV["IN_NIX_SHELL"]?
pending! "Example is broken in Nix shell (#12332)"
end
File.write(source_file, "File.basename(PROGRAM_NAME).inspect(STDOUT)")
compile_file(source_file, bin_name: "×‽😂") do |executable_file|
output = IO::Memory.new
Process.run(executable_file, output: output).success?.should be_true
output.to_s.should eq(File.basename(executable_file).inspect)
end
end
end
end
describe "ARGV" do
it "accepts UTF-8 command-line arguments", tags: %w[slow] do
with_tempfile("source_file") do |source_file|
File.write(source_file, "ARGV.inspect(STDOUT)")
compile_file(source_file) do |executable_file|
args = ["×‽😂", "あ×‽😂い"]
output = IO::Memory.new
Process.run(executable_file, args, output: output).success?.should be_true
output.to_s.should eq(args.inspect)
end
end
end
end
describe "exit" do
it "exits normally with status 0", tags: %w[slow] do
status, _, _ = compile_and_run_source "exit"
status.success?.should be_true
end
it "exits with given error code", tags: %w[slow] do
status, _, _ = compile_and_run_source "exit 42"
status.success?.should be_false
status.exit_code.should eq(42)
end
end
describe "at_exit" do
it "runs handlers on normal program ending", tags: %w[slow] do
status, output, _ = compile_and_run_source <<-CRYSTAL
at_exit do
print "handler code."
end
CRYSTAL
status.success?.should be_true
output.should eq("handler code.")
end
it "runs handlers on explicit program ending", tags: %w[slow] do
status, output, _ = compile_and_run_source <<-'CRYSTAL'
at_exit do |exit_code|
print "handler code, exit code: #{exit_code}."
end
exit 42
CRYSTAL
status.exit_code.should eq(42)
output.should eq("handler code, exit code: 42.")
end
it "runs handlers in reverse order", tags: %w[slow] do
status, output, _ = compile_and_run_source <<-CRYSTAL
at_exit do
print "first handler code."
end
at_exit do
print "second handler code."
end
CRYSTAL
status.success?.should be_true
output.should eq("second handler code.first handler code.")
end
it "runs all handlers maximum once", tags: %w[slow] do
status, output, _ = compile_and_run_source <<-CRYSTAL
at_exit do
print "first handler code."
end
at_exit do
print "second handler code, explicit exit!"
exit
print "not executed."
end
at_exit do
print "third handler code."
end
CRYSTAL
status.success?.should be_true
output.should eq("third handler code.second handler code, explicit exit!first handler code.")
end
it "allows handlers to change the exit code with explicit `exit` call", tags: %w[slow] do
status, output, _ = compile_and_run_source <<-'CRYSTAL'
at_exit do |exit_code|
print "first handler code, exit code: #{exit_code}."
end
at_exit do
print "second handler code, re-exiting."
exit 42
print "not executed."
end
at_exit do |exit_code|
print "third handler code, exit code: #{exit_code}."
end
CRYSTAL
status.success?.should be_false
status.exit_code.should eq(42)
output.should eq("third handler code, exit code: 0.second handler code, re-exiting.first handler code, exit code: 42.")
end
it "allows handlers to change the exit code with explicit `exit` call (2)", tags: %w[slow] do
status, output, _ = compile_and_run_source <<-'CRYSTAL'
at_exit do |exit_code|
print "first handler code, exit code: #{exit_code}."
end
at_exit do
print "second handler code, re-exiting."
exit 42
print "not executed."
end
at_exit do |exit_code|
print "third handler code, exit code: #{exit_code}."
end
exit 21
CRYSTAL
status.success?.should be_false
status.exit_code.should eq(42)
output.should eq("third handler code, exit code: 21.second handler code, re-exiting.first handler code, exit code: 42.")
end
it "changes final exit code when an handler raises an error", tags: %w[slow] do
status, output, error = compile_and_run_source <<-'CRYSTAL'
at_exit do |exit_code|
print "first handler code, exit code: #{exit_code}."
end
at_exit do
print "second handler code, raising."
raise "Raised from at_exit handler!"
print "not executed."
end
at_exit do |exit_code|
print "third handler code, exit code: #{exit_code}."
end
CRYSTAL
status.success?.should be_false
status.exit_code.should eq(1)
output.should eq("third handler code, exit code: 0.second handler code, raising.first handler code, exit code: 1.")
error.should contain("Error running at_exit handler: Raised from at_exit handler!")
end
it "shows unhandled exceptions after at_exit handlers", tags: %w[slow] do
status, _, error = compile_and_run_source <<-CRYSTAL
at_exit do
STDERR.print "first handler code."
end
at_exit do
STDERR.print "second handler code."
end
raise "Kaboom!"
CRYSTAL
status.success?.should be_false
error.should contain("second handler code.first handler code.Unhandled exception: Kaboom!")
end
it "can get unhandled exception in at_exit handler", tags: %w[slow] do
status, _, error = compile_and_run_source <<-CRYSTAL
at_exit do |_, ex|
STDERR.print ex.try &.message
end
raise "Kaboom!"
CRYSTAL
status.success?.should be_false
error.should contain("Kaboom!Unhandled exception: Kaboom!")
end
it "allows at_exit inside at_exit", tags: %w[slow] do
status, output, _ = compile_and_run_source <<-CRYSTAL
at_exit do
print "1"
at_exit do
print "2"
end
end
at_exit do
print "3"
at_exit do
print "4"
end
end
CRYSTAL
status.success?.should be_true
output.should eq("3412")
end
it "prints unhandled exception with cause", tags: %w[slow] do
status, _, error = compile_and_run_source <<-CRYSTAL
raise Exception.new("secondary", cause: Exception.new("primary"))
CRYSTAL
status.success?.should be_false
error.should contain "Unhandled exception: secondary"
error.should contain "Caused by: primary"
end
end
describe "hardware exception" do
it "reports invalid memory access", tags: %w[slow] do
status, _, error = compile_and_run_source <<-'CRYSTAL'
puts Pointer(Int64).null.value
CRYSTAL
status.success?.should be_false
error.should contain("Invalid memory access")
error.should_not contain("Stack overflow")
end
it "detects stack overflow on the main stack", tags: %w[slow] do
# This spec can take some time under FreeBSD where
# the default stack size is 0.5G. Setting a
# smaller stack size with `ulimit -s 8192`
# will address this.
status, _, error = compile_and_run_source <<-'CRYSTAL'
def foo
y = StaticArray(Int8, 512).new(0)
foo
end
foo
CRYSTAL
status.success?.should be_false
error.should contain("Stack overflow")
end
it "detects stack overflow on a fiber stack", tags: %w[slow] do
status, _, error = compile_and_run_source <<-'CRYSTAL'
def foo
y = StaticArray(Int8, 512).new(0)
foo
end
spawn do
foo
end
sleep 60.seconds
CRYSTAL
status.success?.should be_false
error.should contain("Stack overflow")
end
end
|