File: kernel_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 (291 lines) | stat: -rw-r--r-- 7,912 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
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