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
|
require "spec"
require "../support/tempfile"
require "../support/fibers"
require "../support/win32"
require "../support/wasm32"
require "../support/interpreted"
def datapath(*components)
File.join("spec", "std", "data", *components)
end
private class Witness
@checked = false
def check
@checked = true
end
def checked?
@checked
end
end
def spawn_and_wait(before : Proc(_), file = __FILE__, line = __LINE__, &block)
spawn_and_check(before, file, line) do |w|
block.call
w.check
end
end
def spawn_and_check(before : Proc(_), file = __FILE__, line = __LINE__, &block : Witness -> _)
done = Channel(Exception?).new
w = Witness.new
# State of the "before" filter:
# 0 - not started
# 1 - started
# 2 - completed
x = Atomic(Int32).new(0)
before_fiber = spawn do
x.set(1)
# This is a workaround to ensure the "before" fiber
# is unscheduled. Otherwise it might stay alive running the event loop
spawn(same_thread: true) do
while x.get != 2
Fiber.yield
end
end
before.call
x.set(2)
end
spawn do
begin
# Wait until the "before" fiber starts
while x.get == 0
Fiber.yield
end
# Now wait until the "before" fiber is blocked
wait_until_blocked before_fiber
block.call w
done.send nil
rescue e
done.send e
end
end
ex = done.receive
raise ex if ex
unless w.checked?
fail "Failed to stress expected path", file, line
end
end
def compile_file(source_file, *, bin_name = "executable_file", flags = %w(), file = __FILE__, &)
# can't use backtick in interpreted code (#12241)
pending_interpreted! "Unable to compile Crystal code in interpreted code"
with_temp_executable(bin_name, file: file) do |executable_file|
compiler = ENV["CRYSTAL_SPEC_COMPILER_BIN"]? || "bin/crystal"
args = ["build"] + flags + ["-o", executable_file, source_file]
output = IO::Memory.new
status = Process.run(compiler, args, env: {
"CRYSTAL_PATH" => Crystal::PATH,
"CRYSTAL_LIBRARY_PATH" => Crystal::LIBRARY_PATH,
"CRYSTAL_CACHE_DIR" => Crystal::CACHE_DIR,
}, output: output, error: output)
unless status.success?
fail "Compiler command `#{compiler} #{args.join(" ")}` failed with status #{status}.#{"\n" if output}#{output}"
end
File.exists?(executable_file).should be_true
yield executable_file
end
end
def compile_source(source, flags = %w(), file = __FILE__, &)
with_tempfile("source_file", file: file) do |source_file|
File.write(source_file, source)
compile_file(source_file, flags: flags, file: file) do |executable_file|
yield executable_file
end
end
end
def compile_and_run_file(source_file, flags = %w(), runtime_args = %w(), file = __FILE__)
compile_file(source_file, flags: flags, file: file) do |executable_file|
output, error = IO::Memory.new, IO::Memory.new
status = Process.run executable_file, args: runtime_args, output: output, error: error
{status, output.to_s, error.to_s}
end
end
def compile_and_run_source(source, flags = %w(), runtime_args = %w(), file = __FILE__)
with_tempfile("source_file", file: file) do |source_file|
File.write(source_file, source)
compile_and_run_file(source_file, flags, runtime_args, file: file)
end
end
def compile_and_run_source_with_c(c_code, crystal_code, flags = %w(--debug), file = __FILE__, &)
with_temp_c_object_file(c_code, file: file) do |o_filename|
yield compile_and_run_source(%(
require "prelude"
@[Link(ldflags: #{o_filename.inspect})]
#{crystal_code}
))
end
end
|